mirror of
https://github.com/EssentialsX/Essentials.git
synced 2026-11-04 16:50:08 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8724e26ef | ||
|
|
e70bdb817c | ||
|
|
2dbd63adf8 | ||
|
|
cfb6f12da3 | ||
|
|
49a2f10071 | ||
|
|
43cb76a888 | ||
|
|
4600c72fd9 | ||
|
|
d5dc7e3a64 | ||
|
|
8013feb70c | ||
|
|
83aaf8cb37 | ||
|
|
966bc999e3 | ||
|
|
382de4ff3b | ||
|
|
f6f13fdee8 | ||
|
|
d2a7e0f99d | ||
|
|
64cfb10954 | ||
|
|
ee9f6f944a | ||
|
|
776f7094a8 | ||
|
|
28eb89fb02 | ||
|
|
ff54e64909 | ||
|
|
15ec9a2589 | ||
|
|
38f1b4ddde | ||
|
|
37ce087a71 | ||
|
|
e604c9f2ae | ||
|
|
49cc72ced2 | ||
|
|
5e95c87fdf | ||
|
|
c28c654b1e | ||
|
|
26a54475ad | ||
|
|
b303bf90d6 | ||
|
|
100f575293 | ||
|
|
c5a25bb79c | ||
|
|
5baf2391b3 | ||
|
|
c38c9256e9 | ||
|
|
79d67e98c3 | ||
|
|
5db483b548 | ||
|
|
39bc0fe224 | ||
|
|
1a9cba913b | ||
|
|
24b7285e42 | ||
|
|
5c45ee2169 | ||
|
|
87bde72678 | ||
|
|
97fd5609b4 | ||
|
|
936a2c0c62 | ||
|
|
3d03cf35df | ||
|
|
57a8c596f8 | ||
|
|
b594358545 | ||
|
|
e9f3677b95 | ||
|
|
678bacfee5 | ||
|
|
7f37b5e0f3 | ||
|
|
f39bc91ef5 | ||
|
|
63e7c4d259 | ||
|
|
1038b3a597 | ||
|
|
fb669aa9fd | ||
|
|
add5eb7cbf | ||
|
|
ddaadf2099 | ||
|
|
4db09f2a2e | ||
|
|
00b4a5b3bd | ||
|
|
73aa65733e | ||
|
|
86621cd90e | ||
|
|
236ce8837e | ||
|
|
f5fc4305c9 | ||
|
|
08b7fe3bb8 | ||
|
|
0845ea6f08 | ||
|
|
5a78df5494 | ||
|
|
716a61eb16 | ||
|
|
637ac8f4ee | ||
|
|
218e004108 | ||
|
|
c26909e2b8 | ||
|
|
5d535dcbe4 | ||
|
|
8e3b24fdba | ||
|
|
d7452bfcc7 | ||
|
|
482175e6c2 | ||
|
|
302653a77c | ||
|
|
9d70fdb263 | ||
|
|
297d0e4606 | ||
|
|
9c833336fb | ||
|
|
5abe8b61ef | ||
|
|
b8acd9c3ab | ||
|
|
f080f815fa | ||
|
|
18549a718a | ||
|
|
e9b8343751 | ||
|
|
99f7a49f28 | ||
|
|
bc6acfe1dc | ||
|
|
d16aec1525 | ||
|
|
787f539921 | ||
|
|
6ab56d2fe8 | ||
|
|
e5c9074b7c | ||
|
|
77756620cf | ||
|
|
0bd4c24c8b | ||
|
|
18233d3f04 | ||
|
|
5ed1d1e04a | ||
|
|
9ad754b6d9 | ||
|
|
68c304e6b6 | ||
|
|
ac666186f5 | ||
|
|
b34951757b | ||
|
|
000e6c551d | ||
|
|
b0ef56802a | ||
|
|
faa02b5c49 | ||
|
|
0fa6f70b13 | ||
|
|
af44749256 | ||
|
|
87dfe3793a | ||
|
|
9f57b3e476 | ||
|
|
9985dbdd3a | ||
|
|
bb47c45a60 | ||
|
|
150dabbe24 | ||
|
|
adf77dc4ba | ||
|
|
321c911222 | ||
|
|
264b0ed660 | ||
|
|
4dabeb57db | ||
|
|
8982aaec1d | ||
|
|
22b497558a | ||
|
|
65120e9d2c | ||
|
|
893bae02e6 | ||
|
|
ca7f3b5d5d | ||
|
|
973e5f8b27 | ||
|
|
37e10546ea | ||
|
|
0aa25c80b9 | ||
|
|
ca07b531e4 | ||
|
|
e08606432c | ||
|
|
364c87986f | ||
|
|
427f60eb57 | ||
|
|
efbcfb3bd6 | ||
|
|
19f7e99d95 | ||
|
|
e9da11624a | ||
|
|
81ac6f7283 | ||
|
|
7bc91f1c65 | ||
|
|
7c89592bbc | ||
|
|
302dd3fe35 | ||
|
|
69fd7bc8ff | ||
|
|
5ed43f339b | ||
|
|
8ba7a44a7d | ||
|
|
e0e8d4432a | ||
|
|
2ccfad189b | ||
|
|
58726fbc90 | ||
|
|
025ca4895f | ||
|
|
8bcf03d61b | ||
|
|
689c35fb23 | ||
|
|
03d89380cc | ||
|
|
790644b1ab | ||
|
|
334c5a4884 | ||
|
|
d402f17318 | ||
|
|
636bb097fe | ||
|
|
74337fff0f | ||
|
|
b36d43380c | ||
|
|
eb781a192b | ||
|
|
16f643a41f | ||
|
|
c01ba4eec4 | ||
|
|
f4cd1c92e2 | ||
|
|
d002c34fe8 | ||
|
|
ff7c952715 | ||
|
|
0417624bda | ||
|
|
9ef4fc34c3 | ||
|
|
73da278b02 | ||
|
|
e04f4888cd | ||
|
|
dfd83cfb0b | ||
|
|
cc25a798fc | ||
|
|
e39b61447b | ||
|
|
b996a9f143 | ||
|
|
3563b41556 | ||
|
|
ed3c9b83b5 | ||
|
|
6ad737d87a | ||
|
|
8d44d1ae7e | ||
|
|
dae9e3d091 | ||
|
|
573aae01ee | ||
|
|
c43ad269a8 | ||
|
|
e3b4e7723f | ||
|
|
6100cedc58 | ||
|
|
e6b8493749 | ||
|
|
fa4c6b66d5 | ||
|
|
715589c071 | ||
|
|
4a06fd1138 | ||
|
|
25f2f76302 | ||
|
|
ff1b8b8615 | ||
|
|
31ea9036cf | ||
|
|
2796399268 | ||
|
|
4057326b4f | ||
|
|
b44a9b2256 | ||
|
|
4ab3bbca95 | ||
|
|
f5c06b9676 | ||
|
|
997f600c15 | ||
|
|
d42347fb09 | ||
|
|
e836747244 | ||
|
|
3d28d82709 | ||
|
|
3a5c7c44b8 | ||
|
|
aebe851bff | ||
|
|
3fe1495fdb | ||
|
|
3b0c229e78 | ||
|
|
d137031a0a | ||
|
|
a58db237a4 | ||
|
|
fe430174a1 | ||
|
|
3f5b12294f | ||
|
|
be3e641c62 | ||
|
|
84552120c1 | ||
|
|
2bb44386c7 | ||
|
|
06c886ca69 | ||
|
|
f3a1b2b495 | ||
|
|
c7ff994d18 | ||
|
|
f09541cfa6 | ||
|
|
0cb387fb05 | ||
|
|
dabe68701d | ||
|
|
50b696bc07 | ||
|
|
cb6187bbf2 | ||
|
|
40540ebb3d | ||
|
|
46ba679c02 | ||
|
|
2719a189af | ||
|
|
b4a97ab2a9 | ||
|
|
eab220183e | ||
|
|
43d1e37b8f | ||
|
|
80bdc5269f | ||
|
|
90d766df2b | ||
|
|
5bf158c1d4 | ||
|
|
1531cf4146 | ||
|
|
b5748eb32f | ||
|
|
e49021fffe | ||
|
|
ba5257c8e6 | ||
|
|
fe27182ba8 | ||
|
|
5458241fcd | ||
|
|
bad79b79ac | ||
|
|
1ff24cb634 | ||
|
|
f5efa26300 | ||
|
|
263dab54d0 | ||
|
|
e3e247dd61 | ||
|
|
5a839c47bc | ||
|
|
3025ba5c05 | ||
|
|
8313d522dc | ||
|
|
196a2a19c4 | ||
|
|
69ed07a536 | ||
|
|
cb00783ede | ||
|
|
4e6478224c | ||
|
|
fb6edc04c6 | ||
|
|
d232ce4037 | ||
|
|
1a4d75ced7 | ||
|
|
42121aa4be | ||
|
|
ea3ea202a7 | ||
|
|
62fb6a16d4 | ||
|
|
e2295c1e1f | ||
|
|
eb6e192d8d | ||
|
|
b293f2328a | ||
|
|
22de3d8bab | ||
|
|
fe4e388c42 | ||
|
|
803771e74a | ||
|
|
667b0f7523 | ||
|
|
8e53214b50 | ||
|
|
5515f9f3f6 | ||
|
|
f2af9528b0 | ||
|
|
e1091d887d | ||
|
|
424816ef51 | ||
|
|
baa8258936 | ||
|
|
c37064ff74 | ||
|
|
d58db0dce9 | ||
|
|
bc8aa76690 | ||
|
|
d31ff5553e | ||
|
|
b560bbdec4 | ||
|
|
2a41ea050a | ||
|
|
c7cc1b4f93 | ||
|
|
57c9edcc0c | ||
|
|
3f0a412390 | ||
|
|
9590d4cd75 | ||
|
|
f02eeecee8 | ||
|
|
3203e97a5e | ||
|
|
bea43e8798 | ||
|
|
1af1565059 | ||
|
|
38e42f9f90 | ||
|
|
2418a6fb78 | ||
|
|
1778bf5ada | ||
|
|
a87760d3ce | ||
|
|
2ac37d8dd4 | ||
|
|
0b49421bad | ||
|
|
aa2eca17ef | ||
|
|
fe6b04ed84 | ||
|
|
2b209a4a2f | ||
|
|
a4a09299e7 | ||
|
|
4a68758975 | ||
|
|
cb15b467ab | ||
|
|
f7a8f86850 | ||
|
|
cd06e737e1 | ||
|
|
a8452b4615 | ||
|
|
1bede4ae3f | ||
|
|
c80bef9841 | ||
|
|
9c3e0eb312 | ||
|
|
6157668a63 | ||
|
|
04518dbbb3 | ||
|
|
3e29e04722 | ||
|
|
9dfa8cf3cb | ||
|
|
b54c8c1774 | ||
|
|
f1a5caf98e | ||
|
|
7ae01a03ac | ||
|
|
b763fd3868 | ||
|
|
7b02d22ac7 | ||
|
|
8b08a8f2c8 | ||
|
|
80c3150e44 | ||
|
|
33e086ff05 | ||
|
|
8971798a90 | ||
|
|
fcf6e64732 | ||
|
|
9e57dd6aef | ||
|
|
b392f03ad1 | ||
|
|
64531aa862 | ||
|
|
1ea080effa | ||
|
|
6624e6688c | ||
|
|
022b0049b4 | ||
|
|
cecc00a3b6 | ||
|
|
29a122ea5e | ||
|
|
3a6fdd9bf3 | ||
|
|
434e7a0e70 | ||
|
|
e7a4167111 | ||
|
|
aec5859c2a | ||
|
|
5392169f72 | ||
|
|
d4a3f5a9e6 | ||
|
|
bef1e57712 | ||
|
|
4c5a97bf00 | ||
|
|
b9b3e4430e | ||
|
|
84d3138b21 | ||
|
|
1c8060341f | ||
|
|
c58aae78e7 | ||
|
|
cde7184da3 | ||
|
|
549283a1e5 | ||
|
|
6a2527ccdb | ||
|
|
c60ed56190 | ||
|
|
c85e179718 | ||
|
|
71ca7ffbf6 | ||
|
|
76a513a05c | ||
|
|
8cfe680abc | ||
|
|
2cb0af0ca4 | ||
|
|
570eca2e21 | ||
|
|
3817a11d9d | ||
|
|
c19b20ea3b | ||
|
|
a4ac720057 | ||
|
|
993d7ede56 | ||
|
|
622c8147a9 | ||
|
|
6b521e78a6 | ||
|
|
4219450705 | ||
|
|
bd8c792fa4 | ||
|
|
52a638f18f | ||
|
|
1b1b511b61 | ||
|
|
d4b72c8af6 | ||
|
|
185b4e266b | ||
|
|
7a9a0e6f51 | ||
|
|
9be27ad7ac | ||
|
|
09fa6463a7 | ||
|
|
1929d4110a | ||
|
|
4386713807 | ||
|
|
139db29782 | ||
|
|
d307279b3b | ||
|
|
80e6f96c71 | ||
|
|
77d505bb4a | ||
|
|
14125d9c4c | ||
|
|
dfc5c49f56 | ||
|
|
221a6622f3 | ||
|
|
82606a7ced | ||
|
|
1dccfbfcee | ||
|
|
ac0ba8890f | ||
|
|
0cd47639c2 | ||
|
|
63234d6b1d | ||
|
|
8a57856c96 | ||
|
|
3d9d3a3254 | ||
|
|
544f69aec8 | ||
|
|
596ba6e2f5 | ||
|
|
1e040fa7b5 | ||
|
|
51075adec6 | ||
|
|
4134b5fb99 | ||
|
|
d4ecfd2263 | ||
|
|
0d1462a021 | ||
|
|
16e297269d | ||
|
|
3b61b3ed31 | ||
|
|
23093b68d7 | ||
|
|
746627c4b5 | ||
|
|
04c01655f6 | ||
|
|
388d571da9 | ||
|
|
8234dedb22 | ||
|
|
81a8731330 | ||
|
|
fbfd7e9871 | ||
|
|
0af4436c2f | ||
|
|
84e9051f46 | ||
|
|
bb7b334ace | ||
|
|
c68b277782 | ||
|
|
50c4d04b02 | ||
|
|
3eceec87d3 | ||
|
|
fdf1875e7f | ||
|
|
79449ef663 | ||
|
|
b900444ff0 | ||
|
|
a3a71afcef | ||
|
|
72ba87c509 | ||
|
|
ee1111f6aa | ||
|
|
42071f4ad4 | ||
|
|
cf43e643ed | ||
|
|
bb2df0b0a4 | ||
|
|
e3e52db10e | ||
|
|
ad5f17481c | ||
|
|
0a4cf27342 | ||
|
|
dfa22969c9 | ||
|
|
eb76cf0fbb | ||
|
|
409af5d2aa | ||
|
|
6fb500d9fb | ||
|
|
2b993d58cf | ||
|
|
7eed853294 | ||
|
|
6d2a8ffba4 | ||
|
|
b781c1e924 | ||
|
|
19d6db0b4c | ||
|
|
02ced188c8 | ||
|
|
7f3b62a180 | ||
|
|
908b06570f | ||
|
|
d7e5c10b51 | ||
|
|
b2ae28b450 | ||
|
|
e558e7365a | ||
|
|
c32a0f975e | ||
|
|
ae1f1583c2 | ||
|
|
58eea1724f | ||
|
|
52c9ca8197 | ||
|
|
f26e1b2e29 | ||
|
|
f84a311c2b | ||
|
|
60129594a6 | ||
|
|
563d3fab4e | ||
|
|
1022f7a3ab | ||
|
|
d57094c0bc | ||
|
|
863b9cffc5 | ||
|
|
1921abd310 | ||
|
|
803d800bdd | ||
|
|
5276204cf2 | ||
|
|
697128bcf2 | ||
|
|
a1fa1e38f8 | ||
|
|
ed41790712 | ||
|
|
834ef4ea57 | ||
|
|
03ed9a5b41 | ||
|
|
d67dd46cdc | ||
|
|
694ed6e66f | ||
|
|
8a0526b1e5 | ||
|
|
a116e8630d | ||
|
|
8b2c7d7ad1 | ||
|
|
8d07c4bb0f | ||
|
|
fc3dabf8f5 | ||
|
|
cdd277da92 | ||
|
|
0d322e2c6b | ||
|
|
646b60a90b | ||
|
|
357cd6b246 | ||
|
|
a4fb02f3e6 | ||
|
|
2828901927 | ||
|
|
1e0f7cb984 | ||
|
|
52a4dd2c60 | ||
|
|
8323b47e40 | ||
|
|
14cdde009c | ||
|
|
1883e6adbc | ||
|
|
2f9659e7fc | ||
|
|
1a4f63915f | ||
|
|
dd3f4c2921 | ||
|
|
7ebb6359e5 | ||
|
|
069ebfcd5b | ||
|
|
991bc61b0c | ||
|
|
8194d1f747 | ||
|
|
ee7ea2ae39 | ||
|
|
3d23916ad5 | ||
|
|
e5b0c4c855 | ||
|
|
84fd45bd09 | ||
|
|
01883a6b18 | ||
|
|
fd49b70a69 | ||
|
|
3a737480ec | ||
|
|
384f63bf92 | ||
|
|
77dc87bb8e | ||
|
|
c716fc8236 | ||
|
|
c52c1fea9d | ||
|
|
e7c56b32a4 | ||
|
|
11e6d3ca7b | ||
|
|
75481925f7 | ||
|
|
ecb1850451 | ||
|
|
0f4af9e735 | ||
|
|
bf14b88600 | ||
|
|
43d84de2b2 | ||
|
|
25ad8ac9bd | ||
|
|
17051eab73 | ||
|
|
4dc994df3c | ||
|
|
996285c0e3 | ||
|
|
312d1699a8 | ||
|
|
b3238605cc | ||
|
|
b7a4bea52e | ||
|
|
ca71d93963 | ||
|
|
0936fe80bd | ||
|
|
520e8f991f | ||
|
|
3131cadf65 | ||
|
|
bfae6c5c69 | ||
|
|
52c500ee6d | ||
|
|
ba5a8becfc | ||
|
|
2a1957229f | ||
|
|
e6af246170 | ||
|
|
939e5c5a55 | ||
|
|
80bbf8c55b | ||
|
|
376abc4aab | ||
|
|
a7f4d6b16b | ||
|
|
e0d6040662 | ||
|
|
6ec4c97ddd | ||
|
|
db10678095 | ||
|
|
582a357752 | ||
|
|
8a1e7019a7 | ||
|
|
4414eea513 | ||
|
|
6e931b2b3b | ||
|
|
22a0d53cf2 | ||
|
|
20011b9996 | ||
|
|
879e70fb6b | ||
|
|
f91dfed5b4 | ||
|
|
767185ec32 | ||
|
|
4aa93a43db | ||
|
|
57529c4b7d | ||
|
|
dc7fb91939 | ||
|
|
e91ce0c44c | ||
|
|
f7cbc7b0d3 | ||
|
|
28d7abe642 | ||
|
|
0ca58ce4ba | ||
|
|
d75d3cd001 | ||
|
|
1eb5d8a459 | ||
|
|
b808511efe | ||
|
|
0897ef71f2 | ||
|
|
f176893940 | ||
|
|
af18afad7a | ||
|
|
bdc5f4c830 | ||
|
|
0be056b9c2 | ||
|
|
5f5d65c463 | ||
|
|
ca4077b204 | ||
|
|
4ab3e20a0c | ||
|
|
576866540e | ||
|
|
dfcb9a1c52 | ||
|
|
a7a9cbc034 | ||
|
|
2f4f555923 | ||
|
|
1c22edbb1b | ||
|
|
b71fb520aa | ||
|
|
8f7b7d0a5f | ||
|
|
f379323d7a | ||
|
|
f2dde4154c | ||
|
|
12c2a4f2b5 | ||
|
|
ee7c1b1cf1 | ||
|
|
422f25af8b | ||
|
|
728c746200 | ||
|
|
6dfa18ca53 | ||
|
|
a394760677 | ||
|
|
5f48cdef13 | ||
|
|
c56a9e6741 | ||
|
|
5b20f30350 | ||
|
|
037577162b | ||
|
|
908189a233 | ||
|
|
c938767b2d | ||
|
|
b135fcf086 | ||
|
|
09dc49aacb | ||
|
|
f4b6197a49 | ||
|
|
4a53cfe7ac | ||
|
|
2bba577567 | ||
|
|
6e674c73e0 | ||
|
|
a60f3c067f | ||
|
|
6816eb4e18 | ||
|
|
9c5536b2e6 | ||
|
|
ff80338350 | ||
|
|
8d856dd6db | ||
|
|
96c6c951e8 | ||
|
|
0931b21f6d | ||
|
|
9147d1036d | ||
|
|
a7f602e2ad | ||
|
|
0c81980ead | ||
|
|
2d5794f68d | ||
|
|
6b80e0b113 | ||
|
|
18f9af898e | ||
|
|
d04c933abe | ||
|
|
b916488de2 | ||
|
|
c6066bd2d8 | ||
|
|
3af931740b | ||
|
|
1c5a7e9ffb | ||
|
|
ea5cdc097d | ||
|
|
d891268143 | ||
|
|
4bd1b3c09f | ||
|
|
c4f10d9c1d | ||
|
|
e12f1b5022 | ||
|
|
31732cec95 | ||
|
|
f104b6ec15 | ||
|
|
695546420f | ||
|
|
997fb1800e | ||
|
|
3984bddd9d | ||
|
|
d92ce8bdfe | ||
|
|
3348bd9129 | ||
|
|
1a5526867c | ||
|
|
c5253bc5e2 | ||
|
|
29a2a865c5 | ||
|
|
c3b994808f | ||
|
|
bc2da98703 | ||
|
|
cbb012853c | ||
|
|
4bf4bd432a | ||
|
|
83d0e0d9a0 | ||
|
|
2f5c24d114 | ||
|
|
88c8ccd29b | ||
|
|
94edbcfeb1 | ||
|
|
d5822e9a41 | ||
|
|
debf09437e | ||
|
|
a9e5f079a9 | ||
|
|
f00c2dcf92 | ||
|
|
63cbf7e2da | ||
|
|
19837f9309 | ||
|
|
4b8b770c30 | ||
|
|
ddc258ed11 | ||
|
|
d23796dade | ||
|
|
41f5cc3175 | ||
|
|
c6fe160b47 | ||
|
|
5c46bea2ca | ||
|
|
a9585bb525 | ||
|
|
bb88a6a749 | ||
|
|
739600eb05 | ||
|
|
5f98d3fac5 | ||
|
|
84326cf13e | ||
|
|
685084219e | ||
|
|
c4e62ae22b | ||
|
|
05ce11f657 | ||
|
|
bcb0fb5825 | ||
|
|
91f4493fe0 | ||
|
|
d53ff36a2a | ||
|
|
98917d97c8 | ||
|
|
7e1d18fee4 | ||
|
|
0b7e7801d0 | ||
|
|
840035944a | ||
|
|
20a7d1c7ee | ||
|
|
253185bd0e | ||
|
|
dae519494e | ||
|
|
dc27af78bf | ||
|
|
41861a8551 | ||
|
|
9b629e42d8 | ||
|
|
7794634d37 | ||
|
|
d091d6902c | ||
|
|
c45c741210 | ||
|
|
e35c65b976 | ||
|
|
0983167740 | ||
|
|
1509cf8978 | ||
|
|
c0082ee734 | ||
|
|
db2ec1b436 | ||
|
|
7a1e50d10f | ||
|
|
b6ba1768a1 | ||
|
|
c38dd997d2 | ||
|
|
f8ca1bae5d | ||
|
|
3c60bcf2de | ||
|
|
3f857e3183 | ||
|
|
668c9634ca | ||
|
|
51c49b0de6 | ||
|
|
5723dac6ba | ||
|
|
680d3230ea | ||
|
|
8f679de352 | ||
|
|
86d435eee1 | ||
|
|
e4cf965bae | ||
|
|
78c846a0a8 | ||
|
|
c466c9c370 | ||
|
|
6994005a5f | ||
|
|
e43f06bfa6 | ||
|
|
9601687190 | ||
|
|
2ed94f1048 | ||
|
|
70045c30f3 | ||
|
|
ce88c0e1e4 | ||
|
|
494e82e581 | ||
|
|
edd15132fd | ||
|
|
add664441e | ||
|
|
58b0fb089f | ||
|
|
90e4845627 | ||
|
|
43f4d306be | ||
|
|
cb7d9baf7a | ||
|
|
f3aea5e6ec | ||
|
|
a7a1fcd98c | ||
|
|
ef8bcc8bc4 | ||
|
|
b3692d18b0 | ||
|
|
a7b2b675d5 | ||
|
|
7d7a8e47ac | ||
|
|
f0da17b75c | ||
|
|
d86b471402 | ||
|
|
767508fb87 | ||
|
|
c85d772e1b | ||
|
|
fde6524e46 | ||
|
|
b2886969f0 | ||
|
|
19ca5186e4 | ||
|
|
b86a34c909 | ||
|
|
d145fbc5cc | ||
|
|
3ca960e31b | ||
|
|
8bbaf7da68 | ||
|
|
14c2ab5ddd | ||
|
|
6f20a789f9 | ||
|
|
4bee15956a | ||
|
|
e4c179f5ed | ||
|
|
4811eb1558 | ||
|
|
69850d82c8 | ||
|
|
df88a89c14 | ||
|
|
c746a1ce17 | ||
|
|
ecdaed7d11 | ||
|
|
c062651821 | ||
|
|
3692740762 | ||
|
|
1179caab88 | ||
|
|
35b1a284f3 | ||
|
|
36432c65e9 | ||
|
|
871e718f63 | ||
|
|
42293596f3 | ||
|
|
ac8a13f039 | ||
|
|
d56ecaacdc | ||
|
|
14fbfe360e | ||
|
|
5334a3fd34 | ||
|
|
2fa9c6486e | ||
|
|
c221e96122 | ||
|
|
26c016fea2 | ||
|
|
b6a08ec712 | ||
|
|
9179c6c1e2 | ||
|
|
3f9aceb922 | ||
|
|
3da8efae49 | ||
|
|
b84207f955 | ||
|
|
f57de40668 | ||
|
|
e95df57642 | ||
|
|
aaddb2af1f | ||
|
|
082950cc18 | ||
|
|
90828965df | ||
|
|
17e026f9aa | ||
|
|
f1d7e50dc0 | ||
|
|
e2cb7b2102 | ||
|
|
28578a5d8a | ||
|
|
0d8e90b8da | ||
|
|
1556b911b2 | ||
|
|
eed73e3157 | ||
|
|
c020526b1e | ||
|
|
cca98e817f | ||
|
|
46a8a7d04c | ||
|
|
d4ddc87d81 | ||
|
|
b473460ffc | ||
|
|
db027bced3 | ||
|
|
12cc0b99f9 | ||
|
|
15cac529c5 | ||
|
|
feed2d6a45 | ||
|
|
b516db669c | ||
|
|
3bc58264ea | ||
|
|
0a0416ae38 | ||
|
|
d244956b4b | ||
|
|
37e3730e9d | ||
|
|
48532a0ae6 | ||
|
|
0b6b0f9c61 | ||
|
|
435ac80ff9 | ||
|
|
a098055fc9 | ||
|
|
cba5063b33 | ||
|
|
3787b80286 | ||
|
|
2858bd3784 | ||
|
|
ff58d8e443 | ||
|
|
5e178943a0 | ||
|
|
33ca3b2981 | ||
|
|
8c8c85d753 | ||
|
|
6535edf4e0 | ||
|
|
7e25570344 | ||
|
|
409210ccde | ||
|
|
555a62c582 | ||
|
|
36a070be4c | ||
|
|
d903e40a53 | ||
|
|
d78729f343 | ||
|
|
a55289196b | ||
|
|
3505b47d8f | ||
|
|
11836e6662 | ||
|
|
4ce38bde36 | ||
|
|
711f701d62 | ||
|
|
94c509b1e2 | ||
|
|
5c08a0e72c | ||
|
|
9c451271e0 | ||
|
|
55db6c2476 | ||
|
|
0861427bf3 | ||
|
|
9d3bf337e1 | ||
|
|
4e4eb35240 | ||
|
|
e7e3f58c8f | ||
|
|
c18830ce75 | ||
|
|
3f36a52685 | ||
|
|
64eb39a417 | ||
|
|
736ecae5ff | ||
|
|
6bd4a6a037 | ||
|
|
4271938c3a | ||
|
|
e1441c1d33 | ||
|
|
9bd35a2cd2 | ||
|
|
d4e194fbf7 | ||
|
|
003d556f3d | ||
|
|
1da3b3f08e | ||
|
|
f08c176b68 | ||
|
|
780aadedf2 | ||
|
|
3fe279e229 | ||
|
|
c43af5d3d9 | ||
|
|
65b80200bd | ||
|
|
6e5a41a880 | ||
|
|
fa87c74b56 | ||
|
|
8142da608c | ||
|
|
eacc748b09 | ||
|
|
def41802b8 | ||
|
|
38dcdff659 | ||
|
|
bd9540cea3 | ||
|
|
0d71fe65f6 | ||
|
|
54c70581a2 | ||
|
|
3abddd6f84 | ||
|
|
8e712bc827 | ||
|
|
2018149395 | ||
|
|
ecea17195a | ||
|
|
a28d0823b2 | ||
|
|
6e6561a267 | ||
|
|
8b1db22a53 | ||
|
|
3d09ea386b | ||
|
|
300daea4f9 | ||
|
|
7653da0e4f | ||
|
|
19b4da07b9 | ||
|
|
ff9f93fba4 | ||
|
|
fed26071ac | ||
|
|
a4fbfbef02 | ||
|
|
347751a826 | ||
|
|
fbb3a9a0a4 | ||
|
|
f8f1facf43 | ||
|
|
0d6441d0b6 | ||
|
|
a009b2f9b5 | ||
|
|
7852aedabd | ||
|
|
b2d7d1b4cb | ||
|
|
ec50b28f4b | ||
|
|
4ed36d9bbe | ||
|
|
887772a428 | ||
|
|
e9169b890d | ||
|
|
b716c04d96 | ||
|
|
2d3cbc9783 | ||
|
|
23a497abf6 | ||
|
|
d00b90cd61 | ||
|
|
f4fe8808a4 | ||
|
|
02193b0523 | ||
|
|
c9310ea429 | ||
|
|
c76e4239bb | ||
|
|
8a8d45c91e | ||
|
|
071f99560e | ||
|
|
8b23c2c4cd | ||
|
|
0d640d1215 | ||
|
|
b4d63857ce | ||
|
|
4976484d91 | ||
|
|
16627d10b6 | ||
|
|
83ca7d2574 | ||
|
|
956afc0382 | ||
|
|
312ff110ec | ||
|
|
214d07c468 | ||
|
|
2c68d1b866 | ||
|
|
5171215817 | ||
|
|
195148a2a2 | ||
|
|
edf664e19a | ||
|
|
0668e4c48a | ||
|
|
d75787726c | ||
|
|
1e0d7fb0a3 | ||
|
|
454698bf98 | ||
|
|
bfeb0ef2ad | ||
|
|
1cf2b11f1e | ||
|
|
0ce4029483 | ||
|
|
504e82e9fd | ||
|
|
eca6a9f247 | ||
|
|
94d4385d41 | ||
|
|
c41bb0ec42 | ||
|
|
00b71cafc9 | ||
|
|
3cd69dabe9 | ||
|
|
10fa3b5a31 | ||
|
|
b43790e9d2 | ||
|
|
ee5f4b9b42 | ||
|
|
e5db842dd6 | ||
|
|
657a11b1f4 | ||
|
|
0ef1e23c69 | ||
|
|
141512f2f7 | ||
|
|
671983724a | ||
|
|
81571fc014 | ||
|
|
f806409d80 | ||
|
|
f3c347424b | ||
|
|
8cbd9b7935 | ||
|
|
158d273dbe | ||
|
|
1258d2f0a1 | ||
|
|
36422ab22b | ||
|
|
191cea7fb3 | ||
|
|
f8cc0e41d1 | ||
|
|
f29f9e1aba | ||
|
|
34fdcf8f6f | ||
|
|
adef08af3e | ||
|
|
1301e8fc99 | ||
|
|
eee1c0628b | ||
|
|
9ce839c9d8 | ||
|
|
401fc4d2eb | ||
|
|
4e7f1377bf | ||
|
|
fccf796eeb | ||
|
|
500edb7860 | ||
|
|
cb168b0c8a | ||
|
|
74131f816c | ||
|
|
f1ef21eb0d | ||
|
|
79406613c1 | ||
|
|
89c1e3b60d | ||
|
|
224634d8d2 | ||
|
|
e42d37e86d | ||
|
|
191c9e9302 | ||
|
|
a482ddf0f6 | ||
|
|
7245e84f00 | ||
|
|
e287600c34 | ||
|
|
817585a9a6 | ||
|
|
eae8bc05c9 | ||
|
|
026d279556 | ||
|
|
8177893e28 | ||
|
|
70de83827d | ||
|
|
5af9536db1 | ||
|
|
cb7b2dabf5 | ||
|
|
01987a0374 | ||
|
|
5528b5b92c | ||
|
|
1838354b70 | ||
|
|
1f1edd9b23 | ||
|
|
2b7a4364b1 | ||
|
|
2f45d3edf3 | ||
|
|
a20d20574e | ||
|
|
d78832498e | ||
|
|
e2c6170eba | ||
|
|
6864af762c | ||
|
|
a8daa2160f | ||
|
|
b84aaa1dab | ||
|
|
c6de77130f | ||
|
|
02ba924f33 | ||
|
|
318df64e54 | ||
|
|
cb45867de8 | ||
|
|
c4ca0e51e2 | ||
|
|
7bb12fd33c | ||
|
|
001213ee2c | ||
|
|
f320705216 | ||
|
|
d4ed77fdc6 | ||
|
|
ef1202923e | ||
|
|
e1e98f5048 | ||
|
|
5bb3cc88e5 | ||
|
|
43eff69a2f | ||
|
|
450da59057 | ||
|
|
dda4d58a23 | ||
|
|
f5d5ed9844 | ||
|
|
2342d53dba | ||
|
|
005d83c6fe | ||
|
|
8c4fb3fefb | ||
|
|
b0fa0d1ec8 | ||
|
|
2ce5797d5e | ||
|
|
c2617e5a1d | ||
|
|
3f73e31d9e | ||
|
|
84a48adbdd | ||
|
|
63c29a318e | ||
|
|
69748fd72e | ||
|
|
35278c6e90 | ||
|
|
088fc35435 | ||
|
|
4baad6b89c | ||
|
|
9a23f806fe | ||
|
|
82b466db0b |
No files matched your search
@@ -24,7 +24,7 @@
|
||||
|
||||
<!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
|
||||
<module name="SuppressionFilter">
|
||||
<property name="file" value="${checkstyle.suppressions.file}"/>
|
||||
<property name="file" value="${configDirectory}/suppressions.xml"/>
|
||||
</module>
|
||||
|
||||
<!-- https://checkstyle.org/config_filters.html#SuppressWithPlainTextCommentFilter -->
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
<suppressions>
|
||||
<suppress files=".*[\\/]test[\\/]java[\\/].*" checks="RequireExplicitVisibilityModifier"/>
|
||||
<!-- TODO: don't suppress I-prefixed API interfaces under com.earth2me.essentials -->
|
||||
<suppress files="(com[\\/]earth2me[\\/]essentials[\\/]|net[\\/]ess3[\\/])(?:[^\\/]+$|(?!api)[^\\/]+[\\/])" checks="MissingJavadoc(Method|Type)"/>
|
||||
<suppress files="(com[\\/]earth2me[\\/]essentials[\\/]|net[\\/]ess3[\\/]|net[\\/]essentialsx[\\/])(?:[^\\/]+$|(?!api)[^\\/]+[\\/])" checks="MissingJavadoc(Method|Type)"/>
|
||||
</suppressions>
|
||||
@@ -0,0 +1,270 @@
|
||||
---
|
||||
name: mc-update
|
||||
description: Update EssentialsX for a new Minecraft version. Use when the user asks to update to, bump, or support a new Minecraft or Paper version (for example "update to 26.4", "add 26.3 mobs", "bump supported version"), or asks what must be checked for new mobs, items, wood types, armor, enchantments, potions, or Bukkit API changes.
|
||||
---
|
||||
|
||||
# Minecraft update
|
||||
|
||||
This checklist comes from every Minecraft update PR from 1.14 to 26.3, and from the fix PRs that followed them.
|
||||
|
||||
Paths are relative to the repository root. `ESS` means `Essentials/src/main/java/com/earth2me/essentials`.
|
||||
|
||||
## Procedure
|
||||
|
||||
1. Find the old and new `paper-api` jars in `~/.gradle/caches/modules-2/files-2.1/io.papermc.paper/paper-api/`. If the new jar is not there, set `paperVersion` in `build-logic/src/main/kotlin/essentials.base-conventions.gradle.kts` and run `./gradlew :EssentialsX:compileJava` to download it.
|
||||
2. Diff the constants of these classes between the two jars: `org.bukkit.entity.EntityType`, `org.bukkit.Material`, `org.bukkit.enchantments.Enchantment`, `org.bukkit.potion.PotionEffectType`, `org.bukkit.potion.PotionType`, `org.bukkit.TreeType`, and each `*$Variant` class under `org.bukkit.entity`. Use this command for each class:
|
||||
|
||||
```sh
|
||||
unzip -p <jar> org/bukkit/entity/EntityType.class > /tmp/x.class && javap -p /tmp/x.class | grep 'public static final'
|
||||
```
|
||||
|
||||
3. Sort each added, removed, or renamed constant into a section of part 2 or part 3 below.
|
||||
4. Do part 1. Then do each section that applies.
|
||||
5. Apply part 4 to every line you write.
|
||||
6. Run `./gradlew :EssentialsX:compileJava`. Fix errors.
|
||||
7. Do not commit unless the user asks.
|
||||
8. Report per section: what changed, and what you skipped and why.
|
||||
|
||||
---
|
||||
|
||||
## 1. Every update
|
||||
|
||||
### 1.1 Version constants
|
||||
|
||||
1. Open `ESS/utils/VersionUtil.java`.
|
||||
2. Add a constant `vXX_Y_R01 = BukkitVersion.fromString("XX.Y-R0.1-SNAPSHOT")`.
|
||||
- For a patch release, change the string of the existing minor constant. Example: `v26_1_R01` holds `26.1.2`.
|
||||
- For a minor release, add a new constant. Do not rename or delete an old constant. Old constants gate features. Deleting `v1_20_4_R01` caused #6460.
|
||||
3. Add the constant to `supportedVersions`. Keep the `ImmutableSet.of(...)` syntax. `build-logic/src/main/kotlin/VersionDataTask.kt` parses it with a regex.
|
||||
4. Start a server. Confirm `BukkitVersion.fromString(Bukkit.getBukkitVersion())` parses. Spigot 1.14.3 and Paper 26.1 both changed the string format.
|
||||
|
||||
### 1.2 Documentation
|
||||
|
||||
1. Update the supported versions sentence in `README.md`.
|
||||
2. Regenerate `items.json` with ItemDbGenerator.
|
||||
|
||||
---
|
||||
|
||||
## 2. New content
|
||||
|
||||
### 2.1 New mob
|
||||
|
||||
1. Add an entry to `ESS/Mob.java`: `NAME("CamelName", Enemies.X, "ENTITY_TYPE")`.
|
||||
- Use the string constructor. Do not use `EntityType.X`. The constant does not exist on old servers.
|
||||
- Set the plural suffix if it is not `s`. Fish use `""`. Fox uses `es`.
|
||||
- The display name must not contain a space.
|
||||
- Boats and rafts are also `Mob` entries.
|
||||
2. Add `EntityType` constant to `ESS/MobCompat.java` only if `MobData` needs it.
|
||||
3. Add the mob to `protect.prevent.spawn` in `Essentials/src/main/resources/config.yml`.
|
||||
4. Check `entityToDisplayName` in `providers/BaseProviders/.../SpawnerItemProvider.java` if the enum name does not title-case well.
|
||||
|
||||
### 2.2 New mob variant or data
|
||||
|
||||
1. Add entries to `ESS/MobData.java`. Use the string form `"kind:VARIANT"`. Example: `"wolf:PALE"`.
|
||||
2. Add a `case "kind":` to the `setData` switch.
|
||||
3. Add `setKindVariant(Entity, String)` to `MobCompat.java`. Gate it with `VersionUtil.getServerBukkitVersion().isLowerThan(vXX_Y_R01)`.
|
||||
4. Use `RegistryUtil.valueOf(Variant.class, name)` for registry types. Do not use `Enum.valueOf`.
|
||||
5. Do not call `NewClass.Variant.values()` in an enum initializer. Axolotl did this. It threw `NoClassDefFoundError` on 1.16 (#4376).
|
||||
6. Catch `Throwable` around reflective lookups. Cow variant threw NPE on old servers (#6121).
|
||||
7. For a boolean flag, add a `Data` constant and a branch in `setData`. Example: `GOAT_SCREAMING`.
|
||||
|
||||
### 2.3 New rideable or saddle mob
|
||||
|
||||
1. Add `SADDLE_<MOB>` to `MobData.java` with a `Data.<MOB>SADDLE` constant.
|
||||
2. Add `set<Mob>Saddle(Entity, Player)` to `MobCompat.java`. Tame the mob. Set the owner. Set the saddle.
|
||||
3. Check `ESS/craftbukkit/Inventories.java` if the mob adds a player inventory slot. Body and saddle slots (41, 42) broke `/clearinventory` in 1.21.6 (#6218).
|
||||
|
||||
### 2.4 New item or block
|
||||
|
||||
1. Check each `MaterialUtil` set that lists names by hand. See table.
|
||||
2. Check `ESS/utils/LocationUtil.java`:
|
||||
- Add a block that hurts a standing player to `DAMAGING_TYPES`. Magma, campfire, berry bush and wither rose were missed for 2 years (#3537).
|
||||
- `HOLLOW_MATERIALS` comes from `Material.isTransparent()`. Add a passable block that is not transparent. Example: `LIGHT` (#4601). Remove a transparent block that is solid. Examples: `BARRIER`, `DIRT_PATH`, `FARMLAND`.
|
||||
3. Check for a new event that reveals player position. Sculk sensor needed vanish handling in `EssentialsPlayerListener` (#5262).
|
||||
|
||||
| `ESS/utils/MaterialUtil.java` set | Add when |
|
||||
|---|---|
|
||||
| `HELMETS`, `CHESTPLATES`, `LEGGINGS`, `BOOTS` | New armor tier |
|
||||
| `LEATHER_ARMOR` | New dyeable armor |
|
||||
| `BEDS` | New bed. `STRAW_BED` in 26.3. |
|
||||
| `BANNERS` | New banner colour |
|
||||
| `SIGN_POSTS`, `WALL_SIGNS`, `HANGING_SIGNS`, `HANGING_WALL_SIGNS` | New wood type |
|
||||
| `MOB_HEADS` | New head. `PIGLIN_HEAD` in 1.20. |
|
||||
| `POTIONS` | New potion container |
|
||||
| `EDITABLE_BOOKS` | New book type |
|
||||
| `FIREWORKS`, `FIREWORK_CHARGE` | New firework item |
|
||||
|
||||
`EnumUtil.getAllMatching` skips a name that does not exist. Adding a name is safe on old servers.
|
||||
|
||||
### 2.5 Renamed item, block or entity
|
||||
|
||||
1. Grep `ESS` for `Material.OLD_NAME` and `EntityType.OLD_NAME`. Replace with `EnumUtil.getMaterial("NEW", "OLD")` or a `MobCompat` constant. `EntityType.PIG_ZOMBIE` in a static map broke 1.16 (#3412). `EntityType.FIREWORK` broke `/firework` in 1.20.6.
|
||||
2. For a renamed `EntityType`, add `getEntityType("NEW", "OLD")` to `MobCompat.java`. Update `Mob.java`, `MobData.java` and `SpawnMob.java`.
|
||||
|
||||
### 2.6 New wood type
|
||||
|
||||
1. Add `<WOOD>_SIGN`, `<WOOD>_WALL_SIGN`, `<WOOD>_HANGING_SIGN`, `<WOOD>_WALL_HANGING_SIGN` to the four sign sets in `MaterialUtil.java`. Crimson and warped were missed in 1.16 (#3487).
|
||||
2. Add `<WOOD>_BOAT` to `Mob.java`. Boats are separate entity types since 1.21.3.
|
||||
3. Add `<WOOD>` to `MobCompat.BoatVariant` for servers before 1.21.3.
|
||||
4. Add `<WOOD>_BOAT` to `MobData.java`.
|
||||
5. Add `<WOOD>_CHEST_BOAT` to `Mob.java`.
|
||||
|
||||
### 2.7 New tree type
|
||||
|
||||
1. `Commandtree` reads `TreeType.values()`. It needs no code change.
|
||||
2. Add the name to `treeCommandUsage` and `treeCommandUsage1` in `messages.properties`.
|
||||
3. `Commandbigtree` has four hard-coded types. Add a case if the tree has a big variant.
|
||||
4. Bukkit adds `TreeType` late. 26.3-pre-2 has no `POPLAR`.
|
||||
|
||||
### 2.8 New armor tier
|
||||
|
||||
1. Add the four pieces to `HELMETS`, `CHESTPLATES`, `LEGGINGS`, `BOOTS` in `MaterialUtil.java`.
|
||||
2. Add a branch to the armor keyword chain in `ESS/SpawnMob.java`. The chain has `netherite`, `copper`, `diamond`, `gold`, `leather`, `iron`.
|
||||
3. For horse armor, add `<TIER>_HORSE_ARMOR` to `MobData.java`. Example: `NETHERITE_HORSE_ARMOR`.
|
||||
|
||||
### 2.9 New enchantment
|
||||
|
||||
1. Add a `try { // XX.Y }` block to `ESS/Enchantments.java`.
|
||||
2. Look up by name: `Enchantment.getByName("NAME")`. Check for null. Do not use `Enchantment.NAME`.
|
||||
3. Add `ENCHANTMENTS.put("name", e)` and `ALIASENCHANTMENTS.put("alias", e)`.
|
||||
4. Swift sneak (1.19) and lunge (1.21.11) were missed. Contributors added them months later.
|
||||
5. Display and permission names come from `Enchantments.getRealName()`. It uses the key on 1.13+.
|
||||
|
||||
### 2.10 New potion effect or potion type
|
||||
|
||||
1. Add a `try { // XX.Y }` block to `ESS/Potions.java`. Catch `Throwable`.
|
||||
2. Add `POTIONS.put("name", PotionEffectType.NAME)` and `ALIASPOTIONS.put(...)`.
|
||||
3. `ModernPotionMetaProvider` maps `LONG_` and `STRONG_` prefixes. Check a new potion follows that pattern.
|
||||
|
||||
### 2.11 New banner pattern
|
||||
|
||||
No code change. `PatternTypeProvider` reads the registry.
|
||||
|
||||
### 2.12 New colour family
|
||||
|
||||
Check `MaterialUtil.getColorOf` for a colour name that is not a `DyeColor`.
|
||||
|
||||
### 2.13 New spawn egg
|
||||
|
||||
No code change. `FlatSpawnEggProvider` derives the egg from `EntityType.name() + "_SPAWN_EGG"`.
|
||||
|
||||
### 2.14 New command for new content
|
||||
|
||||
1. Gate the command with `VersionUtil`. Return `tl("unsupportedFeature")` on old servers. Examples: `/beezooka`, `/ice`.
|
||||
2. Add the command to `plugin.yml` and `messages.properties`. Check message parameters. `iceOther` shipped without `{0}`.
|
||||
|
||||
---
|
||||
|
||||
## 3. API changes
|
||||
|
||||
### 3.1 Enum becomes a registry
|
||||
|
||||
Enchantment, PotionEffectType, Sound, PatternType, Villager.Profession, Cat.Type, Biome and Boat.Type all changed.
|
||||
|
||||
1. Replace `EnumUtil.valueOf` with `RegistryUtil.valueOf(Class, names...)`. Replace `.values()` with `RegistryUtil.values(Class)`.
|
||||
2. If a static method moved from an enum to an interface, code compiled against the new API throws `IncompatibleClassChangeError` on old servers. `catch (Exception)` does not catch it. `PatternType.getByIdentifier` did this (#6553).
|
||||
3. For such a case, add a provider pair:
|
||||
- Interface in `providers/BaseProviders/src/main/java/net/ess3/provider/`.
|
||||
- `Modern*Provider` in BaseProviders with `@ProviderData(weight = 1)` and a `@ProviderTest`.
|
||||
- `Legacy*Provider` in `providers/1_12Provider` or `providers/1_8Provider`. These compile against the old API.
|
||||
- Register with `providerFactory.registerProvider(Legacy.class, Modern.class)` in `Essentials.java`.
|
||||
4. In a `@ProviderTest`, use `getMethod`, not `getDeclaredMethod`. 1.21 moved spawner methods to a super-interface (80bdc5269).
|
||||
|
||||
### 3.2 Removed API class
|
||||
|
||||
`PotionData`, `Potion`, `BannerMeta.setBaseColor` and `Achievement` were removed.
|
||||
|
||||
1. Move code that uses the removed class to a provider in `1_12Provider` or `1_8Provider`.
|
||||
2. Add a modern provider in BaseProviders.
|
||||
3. Remove the method from `OfflinePlayer`, `FakeWorld` and `FakeServer` if it was an override.
|
||||
|
||||
### 3.3 Changed method signature
|
||||
|
||||
1. Read the Bukkit diff for changed return types on methods EssentialsX calls.
|
||||
2. A changed return type changes the call descriptor. The call throws `NoSuchMethodError` on old servers. `PlayerPickupArrowEvent#getArrow` did this (#3175). `InventoryView` becoming an interface did this (#5851).
|
||||
3. Use a provider or reflection for the changed call.
|
||||
|
||||
### 3.4 New API methods on Player, World, Server
|
||||
|
||||
1. Add stubs to `providers/1_8Provider/.../OfflinePlayer.java` and `providers/1_12Provider/.../FakeWorld.java` if they fail to compile.
|
||||
2. Tests use MockBukkit since 1.21.6. `FakeServer` no longer needs updates.
|
||||
|
||||
### 3.5 New event
|
||||
|
||||
1. Put the handler in a separate `*Listener_XX_Y` class.
|
||||
2. Register it behind a `VersionUtil` check or `Class.forName`.
|
||||
3. Do not put a new event in a listener that loads on old servers. `EntityTransformEvent` failed on 1.13.1 (#2942).
|
||||
|
||||
### 3.6 Spigot NMS obfuscation
|
||||
|
||||
1. Add `VXX_Y_RN` to `providers/NMSReflectionProvider/.../ReflUtil.java` if Spigot bumps the NMS revision.
|
||||
2. Update the obfuscated `MinecraftServer#isRunning` name table in `ReflServerStateProvider.java`. This broke on 1.18, 1.19, 1.19.3, 1.20.6 and 1.21.10.
|
||||
3. Paper uses `PaperServerStateProvider`. Test on Spigot to see this fail.
|
||||
|
||||
### 3.7 Paper internal classes
|
||||
|
||||
Paper config classes move. `ReflOnlineModeProvider` broke on 1.19 when config moved to `io.papermc.paper.configuration.GlobalConfiguration`. Check every reflective Paper access.
|
||||
|
||||
### 3.8 World height
|
||||
|
||||
1. Do not hard-code `0`, `-1`, `128` or `256` as a Y bound.
|
||||
2. Use `WorldInfoProvider.getMinHeight`, `getMaxHeight` and `getLogicalHeight`.
|
||||
3. Grep `LocationUtil`, `RandomTeleport`, `Commandtop`, `Commandsethome`, `Teleport`, `AsyncTeleport` and `SpawnMob`. 1.18 needed three fix PRs (#4657, #4667, #4715).
|
||||
|
||||
### 3.9 Item data components
|
||||
|
||||
1. Check `MetaItemStack.parseStringMeta`. The `[...]` component branch calls `UnsafeValues#modifyItemStack`. Its argument shape changed in 1.20.5 and again in 26.1 (#6570).
|
||||
2. Check `Commandbook`. Writable and written books have separate meta since 1.20.6 (#6064).
|
||||
3. Check `Commandskull`. Skull owner needs a resolved profile since 1.20.4 (#6188).
|
||||
4. Check `MetaItemStack.canSpawn()`.
|
||||
|
||||
### 3.10 Vanilla messages
|
||||
|
||||
Check the vanilla changelog for a new client message that duplicates an EssentialsX message. 1.15 added "Respawn point set". `EssentialsPlayerListener` now gates `bedSet` (#2943).
|
||||
|
||||
### 3.11 Version string format
|
||||
|
||||
1. `BukkitVersion.VERSION_PATTERN` in `VersionUtil.java` must parse the new string.
|
||||
2. Supported suffixes: `-snapshot-N`, `-pre-N`, `-rc-N`, `.build.N-channel`, `.local`, `-R0.1-SNAPSHOT`.
|
||||
3. `compareDevSpecifier` orders `snapshot < pre < rc < release`.
|
||||
4. `isSupportedVersion` compares major, minor and patch. A patch release needs a constant change.
|
||||
5. 26.1 needed four fix PRs for this (#6480, #6483, #6500, #6501).
|
||||
|
||||
### 3.12 Adventure and Paper native paths
|
||||
|
||||
Each Paper API shift produced a gate:
|
||||
- `Essentials.initAdventureFacet` gates the Paper facet at `v1_18_2_R01` (#6562).
|
||||
- `EssentialsSpawnPlayerListener` gates `getRespawnLocation` at `v1_20_4_R01` (#6460).
|
||||
- `EssentialsPlayerListener` gates configuration events at `v1_21_8_R01`.
|
||||
|
||||
Check each gate against the new version.
|
||||
|
||||
---
|
||||
|
||||
## 4. Compatibility rules
|
||||
|
||||
EssentialsX runs on 1.8.8 through the current version. Every update PR must keep that.
|
||||
|
||||
1. Do not reference a new `Material`, `EntityType`, `Enchantment` or `PotionEffectType` constant in a static field or enum initializer. Use `EnumUtil.getMaterial`, `EnumUtil.getEntityType` or a name lookup.
|
||||
2. Catch `Throwable`, not `Exception`, around reflective lookups and provider selection. A swallowed `NoSuchFieldError` selected the wrong provider (7d6195375).
|
||||
3. Use only Guava and Gson methods that exist in 1.8.8. `Ints.constrainToRange` and `JsonArray#add(String)` do not (#4708, 576866540).
|
||||
4. Compile legacy providers in `1_8Provider` or `1_12Provider`. Code compiled against the new API can carry the wrong bytecode.
|
||||
5. Gate with `isLowerThan(<next major>)`, not `isLowerThanOrEqualTo(<patch>)`. Forks use nonstandard revision numbers (43eff69a2).
|
||||
6. Do not delete a `VersionUtil` constant.
|
||||
7. Test on Paper latest, Spigot latest, 1.12.2 and 1.8.8. Most follow-up fixes in this history came from old servers.
|
||||
|
||||
---
|
||||
|
||||
## 5. Test
|
||||
|
||||
1. Run `./gradlew test`.
|
||||
2. Run `./gradlew runServer`.
|
||||
3. Run `/ess version`. Confirm the support status is FULL.
|
||||
4. Run `/spawnmob <newmob>`, `/spawnmob <mob>:<variant>`.
|
||||
5. Run `/give @s <newitem>`, `/give @s minecraft:<newitem>`.
|
||||
6. Run `/enchant <newenchant>`, `/potion <neweffect>`.
|
||||
7. Place an EssentialsX sign on each new sign type.
|
||||
8. Run `/tree <newtree>` if Bukkit has the `TreeType`.
|
||||
9. Run `/kit` with the new armor. Confirm auto-equip.
|
||||
10. Teleport onto each new damaging block. Confirm `/tp` refuses.
|
||||
11. Repeat on Spigot for `ReflServerStateProvider`.
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
# Currently used
|
||||
patreon: EssentialsX
|
||||
ko_fi: essentialsx
|
||||
github: EssentialsX
|
||||
open_collective: EssentialsX
|
||||
|
||||
# Currently not used
|
||||
#github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
#open_collective: # Replace with a single Open Collective username
|
||||
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
#liberapay: # Replace with a single Liberapay username
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: MOSS Discord Server
|
||||
- name: View common issues
|
||||
url: https://github.com/EssentialsX/Essentials/issues/3956
|
||||
about: Check the list of known common issues to see if it solves your problem.
|
||||
- name: Check past help requests
|
||||
url: https://github.com/EssentialsX/Essentials/discussions/categories/q-a
|
||||
about: Visit the Q&A forum to search for previous help requests.
|
||||
- name: Check past feature suggestions
|
||||
url: https://github.com/EssentialsX/Essentials/issues?q=is%3Aissue+label%3A%22type%3A+enhancement%22
|
||||
about: Visit the Feature Suggestions forum to view and discuss existing feature suggestions.
|
||||
- name: Create help request
|
||||
url: https://github.com/EssentialsX/Essentials/discussions/new?category=q-a
|
||||
about: Create a support ticket to get help from developers.
|
||||
- name: Get help from the community on Discord
|
||||
url: https://discord.gg/casfFyh
|
||||
about: Need help with using EssentialsX? Join the MOSS Discord server for help.
|
||||
about: Join the MOSS Discord for community-powered EssentialsX support!
|
||||
@@ -1,86 +0,0 @@
|
||||
---
|
||||
name: Help!
|
||||
about: Encountered a problem with EssentialsX? Not sure how to fix it?
|
||||
labels: 'type: question'
|
||||
|
||||
---
|
||||
|
||||
<!-- EssentialsX help request guide
|
||||
|
||||
NOTE: Failure to fill out this template properly may result in your issue being
|
||||
delayed or ignored.
|
||||
|
||||
Don't type between any arrows in the template, as this text will be hidden.
|
||||
This includes this header block and any other explanatory text blocks.
|
||||
|
||||
Want faster support? Come join our Discord server to get help from both
|
||||
developers and community members: https://discord.gg/F7gexAQ
|
||||
|
||||
If you're happy to wait (or you were sent here from Discord), read on:
|
||||
|
||||
1. Check the Common Issues page.
|
||||
Read through the wiki page to see if you've encountered a regular issue:
|
||||
https://essentialsx.net/wiki/Common-Issues.html
|
||||
|
||||
2. Fill out the template.
|
||||
Run the commands in the console. Don't just put "latest" as a version, or
|
||||
we will ignore it. This will help us understand what problem you've
|
||||
encountered and help us find a solution.
|
||||
|
||||
3. When linking logs or config files, do not attach them to the post!
|
||||
Copy and paste any logs into https://gist.github.com/, then paste a
|
||||
link to them in the relevant parts of the template. Do not use Hastebin
|
||||
or Pastebin, as this can cause issues when trying to solve issues in the
|
||||
future.
|
||||
**DO NOT drag logs into this text box!**
|
||||
|
||||
4. If you are encountering a performance issue, please include a link to a
|
||||
Timings and/or profiler report.
|
||||
|
||||
-->
|
||||
|
||||
### Information
|
||||
|
||||
**Full output of `/ess version`:**
|
||||
<!--
|
||||
Run /ess version in the console, then paste the full output of the command
|
||||
between the ```s.
|
||||
-->
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
**Server startup log:**
|
||||
<!--
|
||||
Restart your server and upload `logs/latest.log` to
|
||||
https://gist.github.com/. You may redact sensitive data (such as IP
|
||||
addresses), but you should make it clear what data was removed. Once
|
||||
uploaded, paste the link below this block.
|
||||
-->
|
||||
|
||||
|
||||
**EssentialsX config:**
|
||||
<!--
|
||||
Upload `plugins/Essentials/config.yml` (and other config files if relevant)
|
||||
to https://gist.github.com/ then paste the link below this block.
|
||||
-->
|
||||
|
||||
|
||||
### Help request
|
||||
|
||||
**Problem:**
|
||||
<!-- What problem did you encounter? Type below this line. -->
|
||||
|
||||
**What I have tried:**
|
||||
<!-- What have you tried so far? Type below this line. -->
|
||||
|
||||
**Console stack trace:**
|
||||
<!--
|
||||
If you are reporting an error in the console, paste it between the ```s.
|
||||
-->
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
**Screenshots:**
|
||||
<!-- If relevant, include any screenshots or a video below this line. -->
|
||||
@@ -1,83 +0,0 @@
|
||||
---
|
||||
name: Report a bug
|
||||
about: Report an EssentialsX bug. Only use this if you're 100% sure there's something wrong with EssentialsX - otherwise, try "Help!".
|
||||
labels: 'bug: unconfirmed'
|
||||
|
||||
---
|
||||
|
||||
<!-- EssentialsX bug reporting guide
|
||||
|
||||
NOTE: Failure to fill out this template properly may result in your issue being
|
||||
delayed or ignored.
|
||||
|
||||
Don't type between any arrows in the template, as this text will be hidden.
|
||||
This includes this header block and any other explanatory text blocks.
|
||||
|
||||
If you are reporting a bug, please follow the following steps:
|
||||
|
||||
1. Fill out the template in full.
|
||||
Run the commands in the console. Don't just put "latest" as a version,
|
||||
or we will ignore it.
|
||||
|
||||
2. When linking files, do not attach them to the post!
|
||||
Copy and paste any logs into https://gist.github.com/, then paste a
|
||||
link to them in the relevant parts of the template. Avoid using
|
||||
Hastebin or Pastebin, as this makes providing support more difficult.
|
||||
**DO NOT drag files into this text box!**
|
||||
|
||||
3. If you are reporting a performance issue, please include a link to a
|
||||
Timings and/or profiler report.
|
||||
|
||||
4. If you are reporting a bug with commands or something else in-game,
|
||||
please include screenshots to help us diagnose the problem.
|
||||
|
||||
-->
|
||||
|
||||
### Information
|
||||
|
||||
**Full output of `/ess version`:**
|
||||
<!--
|
||||
Run /ess version in the console, then paste the full output of the command
|
||||
between the ```s.
|
||||
-->
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
**Server startup log:**
|
||||
<!--
|
||||
Restart your server and upload `logs/latest.log` to
|
||||
https://gist.github.com/. You may redact sensitive data (such as IP
|
||||
addresses), but you should make it clear what data was removed. Once
|
||||
uploaded, paste the link below this block.
|
||||
-->
|
||||
|
||||
|
||||
**EssentialsX config:**
|
||||
<!--
|
||||
Upload `plugins/Essentials/config.yml` (and other config files if relevant)
|
||||
to https://gist.github.com/ then paste the link below this block.
|
||||
-->
|
||||
|
||||
|
||||
### Details
|
||||
|
||||
**Description:**
|
||||
<!-- What is the bug? Type a brief summary below this line. -->
|
||||
|
||||
**Steps to reproduce:**
|
||||
<!-- How did you cause it? Describe what you did to cause the bug below. -->
|
||||
|
||||
**Expected behavior:**
|
||||
<!-- What did you expect to happen? Type below this line. -->
|
||||
|
||||
**Console stack trace:**
|
||||
<!--
|
||||
If you are encountering an error in the console, paste it between the ```s.
|
||||
-->
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
**Screenshots:**
|
||||
<!-- If necessary, include screenshots or a video below this line. -->
|
||||
@@ -0,0 +1,83 @@
|
||||
name: Report bug
|
||||
description: Report a bug in EssentialsX.
|
||||
labels: 'bug: unconfirmed'
|
||||
type: Bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to report an EssentialsX bug! Fill out the form below to provide us with info to help fix the bug.
|
||||
Only use this if you're 100% sure you've found a bug and can reproduce it. If you're looking for general help with EssentialsX, try the Q&A forum or MOSS Discord server.
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Type of bug
|
||||
description: What type of bug is this? Choose all that apply.
|
||||
multiple: true
|
||||
options:
|
||||
- Performance issue or memory leak
|
||||
- Data loss
|
||||
- Exploit
|
||||
- Compatibility issue
|
||||
- Error in console
|
||||
- Other unexpected behaviour
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: "`/ess dump all` output"
|
||||
description: Run `/ess dump all` in the console, then copy and paste the link from the output of the command into this box. (If this command doesn't work, you should [update EssentialsX](https://essentialsx.net/downloads.html) and try again.)
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Error log (if applicable)
|
||||
description: If you are reporting a console error, upload any relevant log excerpts to either https://pastes.dev or https://gist.github.com, save and the paste the link in this box. If you included those files in the same paste as your startup log, paste the same link here.
|
||||
placeholder: "Example: https://pastes.dev/UYL3ASLsee"
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Bug description
|
||||
description: Describe roughly what the bug is here.
|
||||
placeholder: |
|
||||
Example: "When running /nuke after putting everyone into adventure mode, there aren't any explosions..."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Provide an example of how to trigger the bug.
|
||||
placeholder: |
|
||||
Example:
|
||||
1. Have at least 3 people online
|
||||
2. Run `/gma *` to put everyone into adventure mode
|
||||
3. Run `/nuke`
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected behaviour
|
||||
description: Explain what you should expect to happen.
|
||||
placeholder: |
|
||||
Example: "Everything should explode!"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Actual behaviour
|
||||
description: Explain what actually happens.
|
||||
placeholder: |
|
||||
Example: "Everything doesn't explode :("
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional Information
|
||||
description: |
|
||||
In this box, you can attach any relevant screenshots, files and links to Timings/spark profiler reports.
|
||||
You can also include a link to a heapdump if necessary, but please make sure you don't include any private player data in the heapdump.
|
||||
If you suspect this issue is related to a prior issue/PR/commit, please mention it here.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
---
|
||||
name: Request a feature
|
||||
about: Suggest a feature you want to see in EssentialsX!
|
||||
labels: 'type: enhancement'
|
||||
|
||||
---
|
||||
|
||||
<!-- EssentialsX feature request guide
|
||||
|
||||
NOTE: Failure to fill out this template properly may result in your issue being
|
||||
delayed or ignored.
|
||||
|
||||
Don't type between any arrows in the template, as this text will be hidden.
|
||||
This includes this header block and any other explanatory text blocks.
|
||||
|
||||
Not sure if your feature fits in EssentialsX? Feel free to ask on our Discord
|
||||
server: https://discord.gg/F7gexAQ
|
||||
|
||||
If you have a feature suggestion for EssentialsX, read the following tips:
|
||||
|
||||
1. Fill out the template.
|
||||
This will help us understand what you're requesting and why you want us
|
||||
to add it.
|
||||
|
||||
2. Keep it simple.
|
||||
Make sure it's easy to understand what you're requesting. A good way is
|
||||
to keep it to one request per GitHub issue, as we can then easily track
|
||||
feature requests.
|
||||
|
||||
3. Check whether it has already been asked or added.
|
||||
You can search the issue tracker to see if your feature has already been
|
||||
requested at https://github.com/EssentialsX/Essentials/issues. You can
|
||||
also check the changelogs to see if the feature was recently added:
|
||||
https://github.com/EssentialsX/Essentials/releases
|
||||
|
||||
4. Ask yourself: "Does this belong in EssentialsX?"
|
||||
There are lots of features that we reject because most servers won't
|
||||
need or use them. If your feature is very specific or already exists in
|
||||
another plugin, it might not be a good fit for EssentialsX.
|
||||
|
||||
-->
|
||||
|
||||
### Feature request
|
||||
|
||||
**Feature description:**
|
||||
<!-- What feature are you suggesting? Type below this line. -->
|
||||
|
||||
**How the feature is useful:**
|
||||
<!--
|
||||
How is the feature useful to players, server owners and/or developers?
|
||||
Type below this block.
|
||||
-->
|
||||
@@ -0,0 +1,41 @@
|
||||
name: Request a feature
|
||||
description: Suggest a feature you want to see in EssentialsX!
|
||||
labels: 'type: enhancement'
|
||||
type: Feature
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
If you have a feature suggestion for EssentialsX, read the following tips:
|
||||
1. Fill out the template.
|
||||
This will help us understand what you're requesting and why you want us
|
||||
to add it.
|
||||
2. Keep it simple.
|
||||
Make sure it's easy to understand what you're requesting. A good way is
|
||||
to keep it to one request per GitHub issue, as we can then easily track
|
||||
feature requests.
|
||||
3. Check whether it has already been asked or added.
|
||||
You can search the issue tracker to see if your feature has already been
|
||||
requested at https://github.com/EssentialsX/Essentials/issues. You can
|
||||
also check the changelogs to see if the feature was recently added:
|
||||
https://github.com/EssentialsX/Essentials/releases
|
||||
4. Ask yourself: "Does this belong in EssentialsX?"
|
||||
There are lots of features that we reject because most servers won't
|
||||
need or use them. If your feature is very specific or already exists in
|
||||
another plugin, it might not be a good fit for EssentialsX.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Feature description
|
||||
description: Explain what you should expect to happen.
|
||||
placeholder: |
|
||||
What feature are you suggesting?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: How the feature is useful
|
||||
description: Explain what actually happens.
|
||||
placeholder: |
|
||||
How is the feature useful to players, server owners and/or developers?
|
||||
validations:
|
||||
required: true
|
||||
@@ -1,24 +1,30 @@
|
||||
---
|
||||
name: Bug fix
|
||||
about: Use this if your PR fixes a bug in EssentialsX.
|
||||
labels: 'type: bugfix'
|
||||
---
|
||||
<!--
|
||||
|
||||
<!-- EssentialsX bug fix submission guide
|
||||
EssentialsX bug fix submission guide
|
||||
====================================
|
||||
|
||||
NOTE: Failure to fill out this template properly may result in your PR being
|
||||
delayed or ignored without warning.
|
||||
|
||||
Don't type between any arrows in the template, as this text will be hidden.
|
||||
This includes this header block and any other explanatory text blocks.
|
||||
NOTE: Don't type between any arrows in the template, as this text will be
|
||||
hidden. This includes this header block and any other explanation text
|
||||
blocks.
|
||||
|
||||
Want to discuss your PR before submitting it? Join the EssentialsX Development
|
||||
server: https://discord.gg/CUN7qVb
|
||||
|
||||
|
||||
EssentialsX is GPL
|
||||
------------------
|
||||
|
||||
By contributing to EssentialsX, you agree to license your code under the
|
||||
GNU General Public License version 3, which can be found at the link below:
|
||||
https://github.com/EssentialsX/Essentials/blob/2.x/LICENSE
|
||||
|
||||
|
||||
Instructions
|
||||
------------
|
||||
|
||||
If you are submitting a bug fix, please follow the following steps:
|
||||
|
||||
1. Fill out the template in full.
|
||||
@@ -34,14 +40,15 @@ If you are submitting a bug fix, please follow the following steps:
|
||||
Copy and paste any logs into https://gist.github.com/, then paste a
|
||||
link to them in the relevant parts of the template. Do not use Hastebin
|
||||
or Pastebin, as this can cause issues with future reviews.
|
||||
**DO NOT drag logs into this text box!**
|
||||
|
||||
DO NOT drag logs directly into this text box, as we cannot read these!
|
||||
|
||||
3. If you are fixing a performance issue, please include a link to a
|
||||
Timings and/or profiler report, both before and after your PR.
|
||||
|
||||
4. If you are fixing a visual bug, such as in commands, please include
|
||||
screenshots so that we can more easily review the proposed fix.
|
||||
(You may drag screenshots directly into this box.)
|
||||
(You can drag screenshots into the bottom of the editor.)
|
||||
|
||||
-->
|
||||
|
||||
@@ -61,17 +68,21 @@ This PR fixes #nnnn.
|
||||
|
||||
|
||||
**Environments tested:**
|
||||
<!--
|
||||
Below this line, put an "x" inside the box for the environments you have
|
||||
tested this bug fix on, and if relevant alter the OS and Java version
|
||||
accordingly. If this feature does not apply to an environment, strike
|
||||
through the environment using ~~strikethrough~~. If you have tested on
|
||||
other environments, add a new line with relevant details.
|
||||
-->
|
||||
|
||||
- [ ] [Latest](https://papermc.io/downloads) Paper Version (any OS, any Java 8+ version)
|
||||
- [ ] CraftBukkit/Spigot/Paper 1.12.2 (any OS, any Java 8+ version)
|
||||
- [ ] CraftBukkit 1.8.8 (any OS, any Java 8+ version)
|
||||
<!-- Type the OS you have used below. -->
|
||||
OS:
|
||||
|
||||
<!-- Type the JDK version (from java -version) you have used below. -->
|
||||
Java version:
|
||||
|
||||
<!--
|
||||
Put an "x" inside the boxes for the server software you have tested this
|
||||
bug fix on. If this feature does not apply to a server, strike through the server software using ~~strikethrough~~. If you have tested on other
|
||||
environments, add a new line with relevant details.
|
||||
-->
|
||||
- [ ] Most recent Paper version (XX.YY.Z, git-Paper-BUILD)
|
||||
- [ ] CraftBukkit/Spigot/Paper 1.12.2
|
||||
- [ ] CraftBukkit 1.8.8
|
||||
|
||||
|
||||
**Demonstration:**
|
||||
@@ -80,3 +91,4 @@ This PR fixes #nnnn.
|
||||
necessary. If you have created or used a test case plugin, please link to a
|
||||
download of the plugin, source code and exact version used where possible.
|
||||
-->
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
---
|
||||
name: Feature addition
|
||||
about: Use this if your PR adds a new feature to EssentialsX.
|
||||
labels: 'type: enhancement'
|
||||
---
|
||||
<!--
|
||||
|
||||
<!-- EssentialsX feature submission guide
|
||||
EssentialsX feature submission guide
|
||||
====================================
|
||||
|
||||
NOTE: Failure to fill out this template properly may result in your PR being
|
||||
delayed or ignored without warning.
|
||||
|
||||
Don't type between any arrows in the template, as this text will be hidden.
|
||||
This includes this header block and any other explanatory text blocks.
|
||||
NOTE: Don't type between any arrows in the template, as this text will be
|
||||
hidden. This includes this header block and any other explanation text
|
||||
blocks.
|
||||
|
||||
Want to discuss your PR before submitting it? Join the EssentialsX Development
|
||||
server: https://discord.gg/CUN7qVb
|
||||
|
||||
|
||||
EssentialsX is GPL
|
||||
------------------
|
||||
|
||||
By contributing to EssentialsX, you agree to license your code under the
|
||||
GNU General Public License version 3, which can be found at the link below:
|
||||
https://github.com/EssentialsX/Essentials/blob/2.x/LICENSE
|
||||
|
||||
|
||||
Instructions
|
||||
------------
|
||||
|
||||
If you are submitting a new feature, please follow the following steps:
|
||||
|
||||
1. Fill out the template in full.
|
||||
@@ -57,26 +63,31 @@ This PR closes #nnnn.
|
||||
|
||||
### Details
|
||||
|
||||
**Proposed feature**
|
||||
**Proposed feature:**
|
||||
<!-- Type a description of your proposed feature below this line. -->
|
||||
|
||||
**Environments tested:**
|
||||
<!--
|
||||
Below this block, put an "x" inside the box for the environments you have
|
||||
tested this bug fix on, and if relevant alter the OS and Java version
|
||||
accordingly. If this feature does not apply to an environment, strike
|
||||
through the environment using ~~strikethrough~~. If you have tested on
|
||||
other environments, add a new line with relevant details.
|
||||
-->
|
||||
|
||||
- [ ] [Latest](https://papermc.io/downloads) Paper Version (any OS, any Java 8+ version)
|
||||
- [ ] CraftBukkit/Spigot/Paper 1.12.2 (any OS, any Java 8+ version)
|
||||
- [ ] CraftBukkit 1.8.8 (any OS, any Java 8+ version)
|
||||
**Environments tested:**
|
||||
|
||||
<!-- Type the OS you have used below. -->
|
||||
OS:
|
||||
|
||||
<!-- Type the JDK version (from java -version) you have used below. -->
|
||||
Java version:
|
||||
|
||||
<!--
|
||||
Put an "x" inside the boxes for the server software you have tested this
|
||||
bug fix on. If this feature does not apply to a server, strike through the server software using ~~strikethrough~~. If you have tested on other
|
||||
environments, add a new line with relevant details.
|
||||
-->
|
||||
- [ ] Most recent Paper version (XX.YY.Z, git-Paper-BUILD)
|
||||
- [ ] CraftBukkit/Spigot/Paper 1.12.2
|
||||
- [ ] CraftBukkit 1.8.8
|
||||
|
||||
|
||||
**Demonstration:**
|
||||
<!--
|
||||
Below this block, include screenshots/code snippets from before and after
|
||||
as necessary. If you have created or used a test case plugin, please link
|
||||
to a download of the plugin, source code and exact version used where
|
||||
possible.
|
||||
Below this block, include screenshots/log snippets from before and after as
|
||||
necessary. If you have created or used a test case plugin, please link to a
|
||||
download of the plugin, source code and exact version used where possible.
|
||||
-->
|
||||
@@ -0,0 +1,10 @@
|
||||
You need to select a pull request template!
|
||||
|
||||
If you're adding a new feature, copy and paste the following parameter at the end of the URL:
|
||||
?template=new-feature.md
|
||||
|
||||
If you're fixing a bug, copy and paste the following parameter at the end of the URL:
|
||||
?template=bug-fix.md
|
||||
|
||||
For more information about contributing to EssentialsX, see CONTRIBUTING.md:
|
||||
https://github.com/EssentialsX/Essentials/blob/2.x/CONTRIBUTING.md
|
||||
@@ -0,0 +1,151 @@
|
||||
name: Build EssentialsX
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 2.x
|
||||
- dev/*
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and upload
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Git repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK 25
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 25
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
chmod +x gradlew
|
||||
./gradlew build --stacktrace
|
||||
|
||||
- name: Publish JUnit report
|
||||
uses: mikepenz/action-junit-report@v5
|
||||
if: success() || failure() # Run even if the previous step fails
|
||||
with:
|
||||
report_paths: '**/build/test-results/test*/TEST-*.xml'
|
||||
|
||||
- name: Archive plugin jars on GitHub
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: EssentialsX plugin jars
|
||||
path: jars/
|
||||
|
||||
- name: Upload EssentialsX jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsX-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXAntiBuild jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXAntiBuild-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXChat jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXChat-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXDiscord jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXDiscord-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXDiscordLink jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXDiscordLink-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXGeoIP jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXGeoIP-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXProtect jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXProtect-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXSpawn jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXSpawn-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXXMPP jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXXMPP-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Deploy to Maven repo
|
||||
if: ${{ success() && github.event_name == 'push' && github.repository == 'EssentialsX/Essentials' && github.ref == 'refs/heads/2.x' }}
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_essxUsername: ${{ secrets.ESSENTIALSX_DEPLOY_USERNAME }}
|
||||
ORG_GRADLE_PROJECT_essxPassword: ${{ secrets.ESSENTIALSX_DEPLOY_PASSWORD }}
|
||||
run: |
|
||||
./gradlew publish
|
||||
|
||||
- name: Prepare Javadocs
|
||||
run: |
|
||||
mv Essentials/build/docs/javadoc/ javadocs/
|
||||
cp -r EssentialsAntiBuild/build/docs/javadoc/ javadocs/EssentialsAntiBuild/
|
||||
cp -r EssentialsChat/build/docs/javadoc/ javadocs/EssentialsChat/
|
||||
cp -r EssentialsDiscord/build/docs/javadoc/ javadocs/EssentialsDiscord/
|
||||
cp -r EssentialsDiscordLink/build/docs/javadoc/ javadocs/EssentialsDiscordLink/
|
||||
cp -r EssentialsGeoIP/build/docs/javadoc/ javadocs/EssentialsGeoIP/
|
||||
cp -r EssentialsProtect/build/docs/javadoc/ javadocs/EssentialsProtect/
|
||||
cp -r EssentialsSpawn/build/docs/javadoc/ javadocs/EssentialsSpawn/
|
||||
cp -r EssentialsXMPP/build/docs/javadoc/ javadocs/EssentialsXMPP/
|
||||
|
||||
- name: Archive Javadocs
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: javadocs
|
||||
path: javadocs/
|
||||
|
||||
publish-jd:
|
||||
name: Publish Javadocs
|
||||
needs: build
|
||||
if: ${{ github.event_name == 'push' && github.repository == 'EssentialsX/Essentials' && github.ref == 'refs/heads/2.x' }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Download Javadocs
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: javadocs
|
||||
path: javadocs/
|
||||
|
||||
- name: Deploy Javadocs
|
||||
run: npx netlify-cli deploy --dir=javadocs/ --prod --message="GitHubActionsDeploy"
|
||||
env:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_JD_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_JD_2X_SITE_ID }}
|
||||
@@ -0,0 +1,108 @@
|
||||
name: Build Pull Request
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- mc/* # MC version updates
|
||||
- pr/* # PR rebases
|
||||
pull_request:
|
||||
branches:
|
||||
- 2.x
|
||||
- mc/*
|
||||
- dev/*
|
||||
merge_group:
|
||||
types: [checks_requested]
|
||||
|
||||
permissions:
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and upload
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Git repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up JDK 25
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 25
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
chmod +x gradlew
|
||||
./gradlew build --stacktrace
|
||||
|
||||
- name: Publish JUnit report
|
||||
uses: mikepenz/action-junit-report@v4
|
||||
if: success() || failure() # Run even if the previous step fails
|
||||
with:
|
||||
report_paths: '**/build/test-results/test*/TEST-*.xml'
|
||||
|
||||
- name: Archive plugin jars on GitHub
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: EssentialsX plugin jars
|
||||
path: jars/
|
||||
|
||||
- name: Upload EssentialsX jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsX-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXAntiBuild jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXAntiBuild-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXChat jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXChat-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXDiscord jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXDiscord-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXDiscordLink jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXDiscordLink-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXGeoIP jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXGeoIP-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXProtect jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXProtect-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXSpawn jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXSpawn-*.jar
|
||||
archive: false
|
||||
|
||||
- name: Upload EssentialsXXMPP jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
path: jars/EssentialsXXMPP-*.jar
|
||||
archive: false
|
||||
@@ -1,38 +0,0 @@
|
||||
name: Build EssentialsX
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 2.x
|
||||
- mc/*
|
||||
pull_request:
|
||||
branches:
|
||||
- 2.x
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Git repo
|
||||
uses: actions/checkout@v1
|
||||
- name: Restore Maven cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
- name: Set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Build with Maven
|
||||
run: mvn package verify --file pom.xml
|
||||
- name: Copy artifacts
|
||||
run: mkdir -p ./out/ && cp -t ./out/ **/target/Essentials*.jar
|
||||
- uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: Plugin jars
|
||||
path: out/
|
||||
@@ -0,0 +1,112 @@
|
||||
name: Mirror EssentialsX dumps to Gist
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened, edited, closed]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
contents: read
|
||||
|
||||
env:
|
||||
DUMP_REGEX: 'https?://essentialsx\.net/dump\?bytebin=([A-Za-z0-9_-]+)'
|
||||
GIST_LINK_FMT: 'https://essentialsx.net/dump?gist='
|
||||
|
||||
jobs:
|
||||
handle-dump:
|
||||
if: github.event.action != 'closed'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Mirror dump to a private Gist
|
||||
id: mirror
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GIST_TOKEN }}
|
||||
script: |
|
||||
const issue = context.payload.issue ?? context.payload.comment.issue;
|
||||
const bodySrc = context.payload.comment ? context.payload.comment.body : issue.body || '';
|
||||
|
||||
const regex = new RegExp(process.env.DUMP_REGEX);
|
||||
const m = bodySrc.match(regex);
|
||||
if (!m) {
|
||||
core.info('No EssentialsX dump link found – abort.');
|
||||
return;
|
||||
}
|
||||
|
||||
const key = m[1];
|
||||
const dump = await fetch(`https://api.pastes.dev/${key}`);
|
||||
if (!dump.ok) throw new Error(`pastes.dev fetch failed (${dump.status})`);
|
||||
const text = await dump.text();
|
||||
|
||||
const { data: gist } = await github.rest.gists.create({
|
||||
files: { [`${key}.txt`]: { content: text } },
|
||||
description: `EssentialsX dump for issue #${issue.number}`,
|
||||
public: false
|
||||
});
|
||||
|
||||
core.setOutput('link', `${process.env.GIST_LINK_FMT}${gist.id}`);
|
||||
core.setOutput('issue_number', issue.number);
|
||||
|
||||
- name: Comment gist link on issue
|
||||
if: steps.mirror.outputs.link != ''
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: ${{ steps.mirror.outputs.issue_number }},
|
||||
body: `📋 Essentials Dump Backup → ${{ steps.mirror.outputs.link }}`
|
||||
});
|
||||
|
||||
cleanup-gist:
|
||||
if: github.event_name == 'issues' && github.event.action == 'closed'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Delete gist and hide bot comment
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GIST_TOKEN }}
|
||||
script: |
|
||||
const issue = context.payload.issue;
|
||||
|
||||
const comments = await github.paginate(
|
||||
github.rest.issues.listComments,
|
||||
{ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, per_page: 100 }
|
||||
);
|
||||
|
||||
const re = /https?:\/\/essentialsx\.net\/dump\?gist=([A-Za-z0-9]+)/;
|
||||
const hit = comments.reverse().find(c => re.test(c.body || ''));
|
||||
|
||||
if (!hit) {
|
||||
core.info('No gist link comment found – nothing to clean up.');
|
||||
return;
|
||||
}
|
||||
|
||||
const gistId = (hit.body.match(re))[1];
|
||||
const commentNodeId = hit.node_id;
|
||||
|
||||
try {
|
||||
await github.request('DELETE /gists/{gist_id}', { gist_id: gistId });
|
||||
core.info(`Deleted Gist ${gistId}`);
|
||||
} catch (err) {
|
||||
core.warning(`Could not delete Gist ${gistId}: ${err.message}`);
|
||||
}
|
||||
|
||||
try {
|
||||
await github.graphql(
|
||||
`mutation($id:ID!){
|
||||
minimizeComment(input:{subjectId:$id, classifier:OUTDATED}) {
|
||||
minimizedComment { id }
|
||||
}
|
||||
}`,
|
||||
{ id: commentNodeId }
|
||||
);
|
||||
core.info(`Minimised comment ${hit.id} as OUTDATED`);
|
||||
} catch (err) {
|
||||
core.warning(`Could not minimise comment: ${err.message}`);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
name: Command and Permissions Data
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 2.x
|
||||
|
||||
jobs:
|
||||
data:
|
||||
name: Generate and Upload
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Git Repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Generate Data
|
||||
run: |
|
||||
chmod +x gradlew
|
||||
./gradlew commandData
|
||||
./gradlew versionData
|
||||
|
||||
- name: Upload Data
|
||||
uses: ryand56/r2-upload-action@v1
|
||||
with:
|
||||
r2-account-id: ${{ secrets.R2_ACCOUNT_ID }}
|
||||
r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }}
|
||||
r2-secret-access-key: ${{ secrets.R2_SECRET_ACCESS_KEY }}
|
||||
r2-bucket: ${{ secrets.R2_BUCKET }}
|
||||
source-dir: generated
|
||||
destination-dir: ./
|
||||
|
||||
|
||||
+15
-2
@@ -15,8 +15,21 @@
|
||||
/Essentials/kits.yml
|
||||
/Essentials/userdata/testplayer1.yml
|
||||
/Essentials/usermap.csv
|
||||
/Essentials/usermap.bin
|
||||
/Essentials/uuids.bin
|
||||
|
||||
# Build files
|
||||
target/
|
||||
jars/
|
||||
.gradle/
|
||||
/jars/
|
||||
generated/
|
||||
out/
|
||||
build/
|
||||
target/
|
||||
*.class
|
||||
build-logic/.kotlin/
|
||||
build-logic/bin/
|
||||
bin/
|
||||
test-tmp/
|
||||
|
||||
# Run directory
|
||||
/run/
|
||||
Generated
+23
-15
@@ -1,19 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CheckStyle-IDEA">
|
||||
<option name="configuration">
|
||||
<map>
|
||||
<entry key="active-configuration" value="PROJECT_RELATIVE:$PROJECT_DIR$/.checkstyle/checkstyle.xml:EssentialsX" />
|
||||
<entry key="checkstyle-version" value="8.36.2" />
|
||||
<entry key="copy-libs" value="true" />
|
||||
<entry key="location-0" value="BUNDLED:(bundled):Sun Checks" />
|
||||
<entry key="location-1" value="BUNDLED:(bundled):Google Checks" />
|
||||
<entry key="location-2" value="PROJECT_RELATIVE:$PROJECT_DIR$/.checkstyle/checkstyle.xml:EssentialsX" />
|
||||
<entry key="property-2.checkstyle.suppressions.file" value=".checkstyle/suppressions.xml" />
|
||||
<entry key="scan-before-checkin" value="false" />
|
||||
<entry key="scanscope" value="JavaOnly" />
|
||||
<entry key="suppress-errors" value="false" />
|
||||
</map>
|
||||
<component name="CheckStyle-IDEA" serialisationVersion="2">
|
||||
<checkstyleVersion>8.36.2</checkstyleVersion>
|
||||
<scanScope>JavaOnly</scanScope>
|
||||
<copyLibs>true</copyLibs>
|
||||
<scanBeforeCheckin>true</scanBeforeCheckin>
|
||||
<option name="thirdPartyClasspath" />
|
||||
<option name="activeLocationIds">
|
||||
<option value="321e5753-4201-4d2c-bf31-6232011122fd" />
|
||||
</option>
|
||||
<option name="locations">
|
||||
<list>
|
||||
<ConfigurationLocation id="bundled-sun-checks" type="BUNDLED" scope="All" description="Sun Checks">bundled-sun-checks</ConfigurationLocation>
|
||||
<ConfigurationLocation id="bundled-google-checks" type="BUNDLED" scope="All" description="Google Checks">bundled-google-checks</ConfigurationLocation>
|
||||
<ConfigurationLocation id="321e5753-4201-4d2c-bf31-6232011122fd" type="PROJECT_RELATIVE" scope="All" description="EssentialsX">
|
||||
$PROJECT_DIR$/.checkstyle/checkstyle.xml
|
||||
<option name="properties">
|
||||
<map>
|
||||
<entry key="configDirectory" value=".checkstyle/" />
|
||||
</map>
|
||||
</option>
|
||||
</ConfigurationLocation>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
||||
+93
-22
@@ -1,31 +1,102 @@
|
||||
Contributing to EssentialsX
|
||||
===========================
|
||||
# Contributing to EssentialsX
|
||||
|
||||
Want to help improve EssentialsX? There are several ways you can support and contribute to the project.
|
||||
*By contributing to EssentialsX, you agree to license your changes under the [GNU General Public License version 3](https://github.com/EssentialsX/Essentials/blob/2.x/LICENSE).*
|
||||
|
||||
If you'd like to make a financial contribution to the project, you can join our [Patreon](https://www.patreon.com/essentialsx/),
|
||||
or to make a one-off donation you can visit our [Ko-fi page](https://ko-fi.com/essentialsx). If you can't make a
|
||||
donation, don't worry! There are lots of other ways to contribute:
|
||||
## Submitting code changes to EssentialsX
|
||||
|
||||
* Do you run a server? Take a look at our ["help wanted"](https://github.com/EssentialsX/Essentials/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22help+wanted%22)
|
||||
and ["bug: unconfirmed"](https://github.com/EssentialsX/Essentials/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22bug%3A+unconfirmed%22)
|
||||
issues, where you can find issues that need extra testing and investigation.
|
||||
* Do you speak multiple languages? If so, we always welcome contributions to our [Crowdin project](https://crowdin.com/project/essentialsx-official).
|
||||
* Do you enjoy helping others? If so, why not contribute to the [EssentialsX documentation](https://github.com/EssentialsX/wiki)?
|
||||
You can also join the [MOSS Discord community](https://discord.gg/casfFyh) and provide direct community support to
|
||||
other EssentialsX users.
|
||||
* If you're a developer, you could look through our ["open to PR"](https://github.com/EssentialsX/Essentials/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22status%3A+open+to+PR%22)
|
||||
issues. We're always happy to receive bug fixes and feature additions as pull requests.
|
||||
If you're interested in submitting new code to EssentialsX, we accept changes via GitHub pull requests.
|
||||
|
||||
Submitting a PR
|
||||
---------------
|
||||
### Adding features
|
||||
|
||||
EssentialsX has extensive templates for PRs that detail how to submit your PR. To find out more, see the PR templates for:
|
||||
* [Bug fixes](https://github.com/EssentialsX/Essentials/blob/2.x/.github/PULL_REQUEST_TEMPLATE/bug-fix.md)
|
||||
* [New features](https://github.com/EssentialsX/Essentials/blob/2.x/.github/PULL_REQUEST_TEMPLATE/new-feature.md)
|
||||
In general, we will only consider PRs for features if they have already been discussed with the team through
|
||||
GitHub issues and discussions. Check the list of
|
||||
["open to PR" issues](https://github.com/EssentialsX/Essentials/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22status%3A+open+to+PR%22)
|
||||
and [feature request discussions](https://github.com/EssentialsX/Essentials/discussions/categories/ideas-and-feature-suggestions)
|
||||
before you start working on your changes. If you don't see your desired feature listed,
|
||||
[open a feature request](https://github.com/EssentialsX/Essentials/issues/new/choose) and wait for a response, otherwise
|
||||
you may end up wasting your time on a feature that we aren't in a position to accept.
|
||||
|
||||
Want to discuss a feature before opening a PR? Join the [EssentialsX Development Discord server](https://discord.gg/CUN7qVb). Note that this server is **not for end-users** - if you need support with EssentialsX, you should join [MOSS](https://discord.gg/casfFyh) instead.
|
||||
#### Keep it focused
|
||||
|
||||
By contributing to EssentialsX, you agree to license your code under the [GNU General Public License version 3](https://github.com/EssentialsX/Essentials/blob/2.x/LICENSE).
|
||||
Please try to keep feature PRs focused around one feature. Your PR should ideally contain
|
||||
one feature, or a few closely-linked features. If you submit several unrelated features
|
||||
in one PR, the PR will not be accepted.
|
||||
|
||||
### Fixing bugs
|
||||
|
||||
If you're opening a PR to fix a bug, please ensure a bug report has been filed - search the
|
||||
[issue tracker](https://github.com/EssentialsX/Essentials/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22bug%3A+confirmed%22%2C%22bug%3A+unconfirmed%22%2C%22bug%3A+upstream%22)
|
||||
for an existing report, and if you can't find a bug report,
|
||||
[create one](https://github.com/EssentialsX/Essentials/issues/new/choose) before you submit your PR.
|
||||
|
||||
#### Unconfirmed bugs
|
||||
|
||||
You may find a bug report with the label `bug: unconfirmed`. This means the EssentialsX team hasn't had the time
|
||||
to review the bug report yet or hasn't been able to confirm
|
||||
whether the reported issue is actually a bug in EssentialsX. You can help us by following the steps in the report and
|
||||
posting whether you are able to replicate this issue, ideally on the latest versions of EssentialsX and Paper. This will
|
||||
help us confirm issues and prioritise PRs accordingly.
|
||||
|
||||
#### Upstream bugs
|
||||
|
||||
You may also find bug reports with the `bug: upstream` label. This label means the issue is caused by a bug in *another
|
||||
project*, not EssentialsX. This includes bugs in CraftBukkit/Spigot and bugs in other plugins. It may be appropriate to
|
||||
mitigate an issue in EssentialsX, but please check with us in the [EssentialsX Development Discord server](https://discord.gg/CUN7qVb) before you submit a PR.
|
||||
|
||||
### Making your changes
|
||||
|
||||
You'll need the following to make your changes to EssentialsX:
|
||||
|
||||
* A GitHub account
|
||||
* [`git`](https://git-scm.com/downloads): the Git command-line tool, used to track and save your changes
|
||||
* [`gh`](https://cli.github.com/): the GitHub command-line tool, used to submit your changes to EssentialsX
|
||||
* Your IDE of choice - we recommend [IntelliJ IDEA Community Edition](https://www.jetbrains.com/idea/download)
|
||||
|
||||
If you're already familiar with Git, you can skip this next section.
|
||||
|
||||
You'll need to clone the EssentialsX repository
|
||||
(`gh repo clone EssentialsX/Essentials`), then create a new branch for your work
|
||||
(`cd Essentials` then `git switch -c my-cool-pr`).
|
||||
|
||||
You can then make changes using your IDE of choice. Follow the instructions in `README.md` to build and test your
|
||||
changes.
|
||||
|
||||
Once you've finished, you should commit your changes (`git commit -am "My cool commit name!"`).
|
||||
|
||||
### Submitting your PR
|
||||
|
||||
*Even if you're already familiar with GitHub, you will need to follow these steps in order to submit a PR to
|
||||
EssentialsX. This ensures we have the information we need to test and review your changes.*
|
||||
|
||||
Next, you'll need to fork EssentialsX on GitHub and push your changes to a branch on that fork. We strongly recommend
|
||||
using the `gh` command-line tool for this:
|
||||
|
||||
* In your terminal, run `gh pr create`.
|
||||
* Select either the bug fix or feature template, depending on what your PR is for.
|
||||
* If you get asked `Where should we push the '...' branch?`, select `Create a fork of EssentialsX/Essentials`.
|
||||
* For the title, choose a title that describes what your PR does. For example, `Add more ducks to /spawnmob`.
|
||||
* For the body, press E. You will then need to fill in the PR template in your text editor.
|
||||
- Follow the instructions in the template and fill out the sections as required.
|
||||
- This step is important! Without it, we won't know why you've made your changes or how to test them.
|
||||
* Save the file in your text editor and close it, then return to the terminal.
|
||||
* Select `Submit` to open your PR.
|
||||
|
||||
If you follow these steps correctly, GitHub will create a fork (if necessary) and then open a PR with your changes. You
|
||||
can now sit back while we review your changes. If we need to ask questions or request further changes to your code,
|
||||
we will review your PR and post a comment on GitHub. You can respond to our reviews and comments on the GitHub website,
|
||||
and you can push new changes using `git commit` and `git push`.
|
||||
|
||||
|
||||
## Submitting community translations to EssentialsX
|
||||
|
||||
EssentialsX relies on community translations for its messages in other languages. You can help translate EssentialsX on
|
||||
[Crowdin](https://translate.essentialsx.net/). You'll need a Crowdin account to translate and improve messages.
|
||||
|
||||
If your language isn't listed or doesn't have an active proofreader, please let us know on Discord (see below).
|
||||
|
||||
|
||||
## Discussing EssentialsX contributions
|
||||
|
||||
Want to discuss something before opening a PR or translating messages? Join the
|
||||
[EssentialsX Development Discord server](https://discord.gg/CUN7qVb). Note that this server is **not for end-users** -
|
||||
if you need support with EssentialsX, you should join [MOSS](https://discord.gg/casfFyh) instead.
|
||||
@@ -0,0 +1,106 @@
|
||||
plugins {
|
||||
id("essentials.shadow-module")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly('net.essentialsx.deps:VaultAPI:1.7') {
|
||||
exclude group: "org.bukkit", module: "bukkit"
|
||||
}
|
||||
compileOnly 'net.luckperms:api:5.3'
|
||||
|
||||
api 'io.papermc:paperlib:1.0.6'
|
||||
|
||||
implementation 'org.spongepowered:configurate-yaml:4.2.0'
|
||||
implementation 'org.checkerframework:checker-qual:3.49.0'
|
||||
implementation 'nu.studer:java-ordered-properties:1.0.4'
|
||||
|
||||
implementation 'net.kyori:adventure-api:4.25.0'
|
||||
implementation 'net.kyori:adventure-text-minimessage:4.25.0'
|
||||
implementation 'net.kyori:adventure-platform-bukkit:4.4.1'
|
||||
|
||||
// Providers
|
||||
api(project(':providers:BaseProviders')) {
|
||||
exclude(module: 'spigot-api')
|
||||
}
|
||||
api(project(path: ':providers:PaperProvider', configuration: 'shadow')){
|
||||
exclude(module: 'paper-api')
|
||||
}
|
||||
api(project(':providers:NMSReflectionProvider')) {
|
||||
exclude(module: 'bukkit')
|
||||
}
|
||||
api(project(':providers:1_8Provider')) {
|
||||
exclude(module: 'spigot-api')
|
||||
}
|
||||
api(project(':providers:1_12Provider')) {
|
||||
exclude(module: 'bukkit')
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
testLogging.showStandardStreams = true
|
||||
}
|
||||
|
||||
tasks.register('versionData', VersionDataTask) {
|
||||
destination.set(rootProject.layout.projectDirectory.dir("generated").file("version-data.json"))
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
dependencies {
|
||||
include (dependency('io.papermc:paperlib'))
|
||||
include (dependency('org.bstats:bstats-bukkit'))
|
||||
include (dependency('org.bstats:bstats-base'))
|
||||
include (dependency('org.spongepowered:configurate-yaml'))
|
||||
include (dependency('org.spongepowered:configurate-core'))
|
||||
include (dependency('org.yaml:snakeyaml'))
|
||||
include (dependency('io.leangen.geantyref:geantyref'))
|
||||
include (dependency('org.checkerframework:checker-qual'))
|
||||
include (dependency('nu.studer:java-ordered-properties'))
|
||||
include (dependency('net.kyori:adventure-api'))
|
||||
include (dependency('net.kyori:adventure-key'))
|
||||
include (dependency('net.kyori:examination-api'))
|
||||
include (dependency('net.kyori:examination-string'))
|
||||
include (dependency('net.kyori:option'))
|
||||
include (dependency('net.kyori:adventure-platform-bukkit'))
|
||||
include (dependency('net.kyori:adventure-platform-api'))
|
||||
include (dependency('net.kyori:adventure-platform-facet'))
|
||||
include (dependency('net.kyori:adventure-nbt'))
|
||||
include (dependency('net.kyori:adventure-text-serializer-bungeecord'))
|
||||
include (dependency('net.kyori:adventure-text-serializer-gson'))
|
||||
include (dependency('net.kyori:adventure-text-serializer-gson-legacy-impl'))
|
||||
include (dependency('net.kyori:adventure-text-serializer-json'))
|
||||
include (dependency('net.kyori:adventure-text-serializer-json-legacy-impl'))
|
||||
include (dependency('net.kyori:adventure-text-serializer-legacy'))
|
||||
include (dependency('net.kyori:adventure-text-minimessage'))
|
||||
include (project(':providers:BaseProviders'))
|
||||
include (project(path: ':providers:PaperProvider', configuration: 'shadow'))
|
||||
include (project(':providers:NMSReflectionProvider'))
|
||||
include (project(':providers:1_8Provider'))
|
||||
include (project(':providers:1_12Provider'))
|
||||
|
||||
// Paper in-dev adventure versions (for snapshot/pre-releases)
|
||||
include (dependency('io.papermc.adventure:adventure-api'))
|
||||
include (dependency('io.papermc.adventure:adventure-key'))
|
||||
include (dependency('io.papermc.adventure:adventure-nbt'))
|
||||
include (dependency('io.papermc.adventure:adventure-text-serializer-gson'))
|
||||
include (dependency('io.papermc.adventure:adventure-text-serializer-gson-legacy-impl'))
|
||||
include (dependency('io.papermc.adventure:adventure-text-serializer-json'))
|
||||
include (dependency('io.papermc.adventure:adventure-text-serializer-json-legacy-impl'))
|
||||
include (dependency('io.papermc.adventure:adventure-text-serializer-legacy'))
|
||||
include (dependency('io.papermc.adventure:adventure-text-minimessage'))
|
||||
}
|
||||
relocate 'io.papermc.lib', 'com.earth2me.essentials.paperlib'
|
||||
relocate 'org.bstats', 'com.earth2me.essentials.libs.bstats'
|
||||
relocate 'org.spongepowered.configurate', 'com.earth2me.essentials.libs.configurate'
|
||||
relocate 'org.yaml.snakeyaml', 'com.earth2me.essentials.libs.snakeyaml'
|
||||
relocate 'io.leangen.geantyref', 'com.earth2me.essentials.libs.geantyref'
|
||||
relocate 'org.checkerframework', 'com.earth2me.essentials.libs.checkerframework'
|
||||
relocate 'net.kyori', 'com.earth2me.essentials.libs.kyori'
|
||||
relocate 'net.essentialsx.temp.adventure', 'net.kyori.adventure' // Undo temporary relocation for Paper Provider
|
||||
|
||||
minimize {
|
||||
include(dependency('org.checkerframework:checker-qual'))
|
||||
include(dependency('net.kyori:adventure-api'))
|
||||
include(dependency('net.kyori:adventure-platform-bukkit'))
|
||||
include(dependency('net.kyori:adventure-text-minimessage'))
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>net.ess3</groupId>
|
||||
<artifactId>EssentialsXParent</artifactId>
|
||||
<version>2.18.2</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>EssentialsX</artifactId>
|
||||
|
||||
<build>
|
||||
<finalName>EssentialsX-${full.version}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
<configuration>
|
||||
<showDeprecation>false</showDeprecation>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>io.papermc.lib</pattern>
|
||||
<shadedPattern>com.earth2me.essentials.paperlib</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.milkbowl</groupId>
|
||||
<artifactId>VaultAPI</artifactId>
|
||||
<version>1.7</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.luckperms</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>5.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.papermc</groupId>
|
||||
<artifactId>paperlib</artifactId>
|
||||
<version>1.0.6-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.ess3</groupId>
|
||||
<artifactId>BaseProviders</artifactId>
|
||||
<version>2.18.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.ess3</groupId>
|
||||
<artifactId>NMSReflectionProvider</artifactId>
|
||||
<version>2.18.2</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.ess3</groupId>
|
||||
<artifactId>PaperProvider</artifactId>
|
||||
<version>2.18.2</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.destroystokyo.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.ess3</groupId>
|
||||
<artifactId>1_8Provider</artifactId>
|
||||
<version>2.18.2</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.bukkit</groupId>
|
||||
<artifactId>bukkit</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,108 +0,0 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import net.ess3.api.IEssentials;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.command.PluginCommandYamlParser;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class AlternativeCommandsHandler {
|
||||
private static final Logger LOGGER = Logger.getLogger("Essentials");
|
||||
private final transient Map<String, List<PluginCommand>> altcommands = new HashMap<>();
|
||||
private final transient Map<String, String> disabledList = new HashMap<>();
|
||||
private final transient IEssentials ess;
|
||||
|
||||
public AlternativeCommandsHandler(final IEssentials ess) {
|
||||
this.ess = ess;
|
||||
for (final Plugin plugin : ess.getServer().getPluginManager().getPlugins()) {
|
||||
if (plugin.isEnabled()) {
|
||||
addPlugin(plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void addPlugin(final Plugin plugin) {
|
||||
if (plugin.getDescription().getMain().contains("com.earth2me.essentials")) {
|
||||
return;
|
||||
}
|
||||
final List<Command> commands = PluginCommandYamlParser.parse(plugin);
|
||||
final String pluginName = plugin.getDescription().getName().toLowerCase(Locale.ENGLISH);
|
||||
|
||||
for (final Command command : commands) {
|
||||
final PluginCommand pc = (PluginCommand) command;
|
||||
final List<String> labels = new ArrayList<>(pc.getAliases());
|
||||
labels.add(pc.getName());
|
||||
|
||||
PluginCommand reg = ess.getServer().getPluginCommand(pluginName + ":" + pc.getName().toLowerCase(Locale.ENGLISH));
|
||||
if (reg == null) {
|
||||
reg = ess.getServer().getPluginCommand(pc.getName().toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
if (reg == null || !reg.getPlugin().equals(plugin)) {
|
||||
continue;
|
||||
}
|
||||
for (final String label : labels) {
|
||||
final List<PluginCommand> plugincommands = altcommands.computeIfAbsent(label.toLowerCase(Locale.ENGLISH), k -> new ArrayList<>());
|
||||
boolean found = false;
|
||||
for (final PluginCommand pc2 : plugincommands) {
|
||||
if (pc2.getPlugin().equals(plugin)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
plugincommands.add(reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removePlugin(final Plugin plugin) {
|
||||
final Iterator<Map.Entry<String, List<PluginCommand>>> iterator = altcommands.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
final Map.Entry<String, List<PluginCommand>> entry = iterator.next();
|
||||
entry.getValue().removeIf(pc -> pc.getPlugin() == null || pc.getPlugin().equals(plugin));
|
||||
if (entry.getValue().isEmpty()) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public PluginCommand getAlternative(final String label) {
|
||||
final List<PluginCommand> commands = altcommands.get(label);
|
||||
if (commands == null || commands.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (commands.size() == 1) {
|
||||
return commands.get(0);
|
||||
}
|
||||
// return the first command that is not an alias
|
||||
for (final PluginCommand command : commands) {
|
||||
if (command.getName().equalsIgnoreCase(label)) {
|
||||
return command;
|
||||
}
|
||||
}
|
||||
// return the first alias
|
||||
return commands.get(0);
|
||||
}
|
||||
|
||||
public void executed(final String label, final PluginCommand pc) {
|
||||
final String altString = pc.getPlugin().getName() + ":" + pc.getLabel();
|
||||
if (ess.getSettings().isDebug()) {
|
||||
LOGGER.log(Level.INFO, "Essentials: Alternative command " + label + " found, using " + altString);
|
||||
}
|
||||
disabledList.put(label, altString);
|
||||
}
|
||||
|
||||
public Map<String, String> disabledCommands() {
|
||||
return disabledList;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
public class ChargeException extends Exception {
|
||||
public ChargeException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ChargeException(final String message, final Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class CommandSource {
|
||||
protected CommandSender sender;
|
||||
|
||||
public CommandSource(final CommandSender base) {
|
||||
this.sender = base;
|
||||
}
|
||||
|
||||
public final CommandSender getSender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
public final Player getPlayer() {
|
||||
if (sender instanceof Player) {
|
||||
return (Player) sender;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public final net.ess3.api.IUser getUser(final IEssentials ess) {
|
||||
if (sender instanceof Player) {
|
||||
return ess.getUser((Player) sender);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public final boolean isPlayer() {
|
||||
return sender instanceof Player;
|
||||
}
|
||||
|
||||
public final CommandSender setSender(final CommandSender base) {
|
||||
return this.sender = base;
|
||||
}
|
||||
|
||||
public void sendMessage(final String message) {
|
||||
if (!message.isEmpty()) {
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAuthorized(final String permission, final IEssentials ess) {
|
||||
return !(sender instanceof Player) || getUser(ess).isAuthorized(permission);
|
||||
}
|
||||
|
||||
public String getSelfSelector() {
|
||||
return sender instanceof Player ? getPlayer().getName() : "*";
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return sender instanceof Player ? getPlayer().getDisplayName() : getSender().getName();
|
||||
}
|
||||
}
|
||||
@@ -1,304 +0,0 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
public final class Enchantments {
|
||||
private static final Map<String, Enchantment> ENCHANTMENTS = new HashMap<>();
|
||||
private static final Map<String, Enchantment> ALIASENCHANTMENTS = new HashMap<>();
|
||||
private static boolean isFlat;
|
||||
|
||||
static {
|
||||
ENCHANTMENTS.put("alldamage", Enchantment.DAMAGE_ALL);
|
||||
ALIASENCHANTMENTS.put("alldmg", Enchantment.DAMAGE_ALL);
|
||||
ENCHANTMENTS.put("sharpness", Enchantment.DAMAGE_ALL);
|
||||
ALIASENCHANTMENTS.put("sharp", Enchantment.DAMAGE_ALL);
|
||||
ALIASENCHANTMENTS.put("dal", Enchantment.DAMAGE_ALL);
|
||||
|
||||
ENCHANTMENTS.put("ardmg", Enchantment.DAMAGE_ARTHROPODS);
|
||||
ENCHANTMENTS.put("baneofarthropods", Enchantment.DAMAGE_ARTHROPODS);
|
||||
ALIASENCHANTMENTS.put("baneofarthropod", Enchantment.DAMAGE_ARTHROPODS);
|
||||
ALIASENCHANTMENTS.put("arthropod", Enchantment.DAMAGE_ARTHROPODS);
|
||||
ALIASENCHANTMENTS.put("dar", Enchantment.DAMAGE_ARTHROPODS);
|
||||
|
||||
ENCHANTMENTS.put("undeaddamage", Enchantment.DAMAGE_UNDEAD);
|
||||
ENCHANTMENTS.put("smite", Enchantment.DAMAGE_UNDEAD);
|
||||
ALIASENCHANTMENTS.put("du", Enchantment.DAMAGE_UNDEAD);
|
||||
|
||||
ENCHANTMENTS.put("digspeed", Enchantment.DIG_SPEED);
|
||||
ENCHANTMENTS.put("efficiency", Enchantment.DIG_SPEED);
|
||||
ALIASENCHANTMENTS.put("minespeed", Enchantment.DIG_SPEED);
|
||||
ALIASENCHANTMENTS.put("cutspeed", Enchantment.DIG_SPEED);
|
||||
ALIASENCHANTMENTS.put("ds", Enchantment.DIG_SPEED);
|
||||
ALIASENCHANTMENTS.put("eff", Enchantment.DIG_SPEED);
|
||||
|
||||
ENCHANTMENTS.put("durability", Enchantment.DURABILITY);
|
||||
ALIASENCHANTMENTS.put("dura", Enchantment.DURABILITY);
|
||||
ENCHANTMENTS.put("unbreaking", Enchantment.DURABILITY);
|
||||
ALIASENCHANTMENTS.put("d", Enchantment.DURABILITY);
|
||||
|
||||
ENCHANTMENTS.put("thorns", Enchantment.THORNS);
|
||||
ENCHANTMENTS.put("highcrit", Enchantment.THORNS);
|
||||
ALIASENCHANTMENTS.put("thorn", Enchantment.THORNS);
|
||||
ALIASENCHANTMENTS.put("highercrit", Enchantment.THORNS);
|
||||
ALIASENCHANTMENTS.put("t", Enchantment.THORNS);
|
||||
|
||||
ENCHANTMENTS.put("fireaspect", Enchantment.FIRE_ASPECT);
|
||||
ENCHANTMENTS.put("fire", Enchantment.FIRE_ASPECT);
|
||||
ALIASENCHANTMENTS.put("meleefire", Enchantment.FIRE_ASPECT);
|
||||
ALIASENCHANTMENTS.put("meleeflame", Enchantment.FIRE_ASPECT);
|
||||
ALIASENCHANTMENTS.put("fa", Enchantment.FIRE_ASPECT);
|
||||
|
||||
ENCHANTMENTS.put("knockback", Enchantment.KNOCKBACK);
|
||||
ALIASENCHANTMENTS.put("kback", Enchantment.KNOCKBACK);
|
||||
ALIASENCHANTMENTS.put("kb", Enchantment.KNOCKBACK);
|
||||
ALIASENCHANTMENTS.put("k", Enchantment.KNOCKBACK);
|
||||
|
||||
ALIASENCHANTMENTS.put("blockslootbonus", Enchantment.LOOT_BONUS_BLOCKS);
|
||||
ENCHANTMENTS.put("fortune", Enchantment.LOOT_BONUS_BLOCKS);
|
||||
ALIASENCHANTMENTS.put("fort", Enchantment.LOOT_BONUS_BLOCKS);
|
||||
ALIASENCHANTMENTS.put("lbb", Enchantment.LOOT_BONUS_BLOCKS);
|
||||
|
||||
ALIASENCHANTMENTS.put("mobslootbonus", Enchantment.LOOT_BONUS_MOBS);
|
||||
ENCHANTMENTS.put("mobloot", Enchantment.LOOT_BONUS_MOBS);
|
||||
ENCHANTMENTS.put("looting", Enchantment.LOOT_BONUS_MOBS);
|
||||
ALIASENCHANTMENTS.put("lbm", Enchantment.LOOT_BONUS_MOBS);
|
||||
|
||||
ALIASENCHANTMENTS.put("oxygen", Enchantment.OXYGEN);
|
||||
ENCHANTMENTS.put("respiration", Enchantment.OXYGEN);
|
||||
ALIASENCHANTMENTS.put("breathing", Enchantment.OXYGEN);
|
||||
ENCHANTMENTS.put("breath", Enchantment.OXYGEN);
|
||||
ALIASENCHANTMENTS.put("o", Enchantment.OXYGEN);
|
||||
|
||||
ENCHANTMENTS.put("protection", Enchantment.PROTECTION_ENVIRONMENTAL);
|
||||
ALIASENCHANTMENTS.put("prot", Enchantment.PROTECTION_ENVIRONMENTAL);
|
||||
ENCHANTMENTS.put("protect", Enchantment.PROTECTION_ENVIRONMENTAL);
|
||||
ALIASENCHANTMENTS.put("p", Enchantment.PROTECTION_ENVIRONMENTAL);
|
||||
|
||||
ALIASENCHANTMENTS.put("explosionsprotection", Enchantment.PROTECTION_EXPLOSIONS);
|
||||
ALIASENCHANTMENTS.put("explosionprotection", Enchantment.PROTECTION_EXPLOSIONS);
|
||||
ALIASENCHANTMENTS.put("expprot", Enchantment.PROTECTION_EXPLOSIONS);
|
||||
ALIASENCHANTMENTS.put("blastprotection", Enchantment.PROTECTION_EXPLOSIONS);
|
||||
ALIASENCHANTMENTS.put("bprotection", Enchantment.PROTECTION_EXPLOSIONS);
|
||||
ALIASENCHANTMENTS.put("bprotect", Enchantment.PROTECTION_EXPLOSIONS);
|
||||
ENCHANTMENTS.put("blastprotect", Enchantment.PROTECTION_EXPLOSIONS);
|
||||
ALIASENCHANTMENTS.put("pe", Enchantment.PROTECTION_EXPLOSIONS);
|
||||
|
||||
ALIASENCHANTMENTS.put("fallprotection", Enchantment.PROTECTION_FALL);
|
||||
ENCHANTMENTS.put("fallprot", Enchantment.PROTECTION_FALL);
|
||||
ENCHANTMENTS.put("featherfall", Enchantment.PROTECTION_FALL);
|
||||
ALIASENCHANTMENTS.put("featherfalling", Enchantment.PROTECTION_FALL);
|
||||
ALIASENCHANTMENTS.put("pfa", Enchantment.PROTECTION_FALL);
|
||||
|
||||
ALIASENCHANTMENTS.put("fireprotection", Enchantment.PROTECTION_FIRE);
|
||||
ALIASENCHANTMENTS.put("flameprotection", Enchantment.PROTECTION_FIRE);
|
||||
ENCHANTMENTS.put("fireprotect", Enchantment.PROTECTION_FIRE);
|
||||
ALIASENCHANTMENTS.put("flameprotect", Enchantment.PROTECTION_FIRE);
|
||||
ENCHANTMENTS.put("fireprot", Enchantment.PROTECTION_FIRE);
|
||||
ALIASENCHANTMENTS.put("flameprot", Enchantment.PROTECTION_FIRE);
|
||||
ALIASENCHANTMENTS.put("pf", Enchantment.PROTECTION_FIRE);
|
||||
|
||||
ENCHANTMENTS.put("projectileprotection", Enchantment.PROTECTION_PROJECTILE);
|
||||
ENCHANTMENTS.put("projprot", Enchantment.PROTECTION_PROJECTILE);
|
||||
ALIASENCHANTMENTS.put("pp", Enchantment.PROTECTION_PROJECTILE);
|
||||
|
||||
ENCHANTMENTS.put("silktouch", Enchantment.SILK_TOUCH);
|
||||
ALIASENCHANTMENTS.put("softtouch", Enchantment.SILK_TOUCH);
|
||||
ALIASENCHANTMENTS.put("st", Enchantment.SILK_TOUCH);
|
||||
|
||||
ENCHANTMENTS.put("waterworker", Enchantment.WATER_WORKER);
|
||||
ENCHANTMENTS.put("aquaaffinity", Enchantment.WATER_WORKER);
|
||||
ALIASENCHANTMENTS.put("watermine", Enchantment.WATER_WORKER);
|
||||
ALIASENCHANTMENTS.put("ww", Enchantment.WATER_WORKER);
|
||||
|
||||
ALIASENCHANTMENTS.put("firearrow", Enchantment.ARROW_FIRE);
|
||||
ENCHANTMENTS.put("flame", Enchantment.ARROW_FIRE);
|
||||
ENCHANTMENTS.put("flamearrow", Enchantment.ARROW_FIRE);
|
||||
ALIASENCHANTMENTS.put("af", Enchantment.ARROW_FIRE);
|
||||
|
||||
ENCHANTMENTS.put("arrowdamage", Enchantment.ARROW_DAMAGE);
|
||||
ENCHANTMENTS.put("power", Enchantment.ARROW_DAMAGE);
|
||||
ALIASENCHANTMENTS.put("arrowpower", Enchantment.ARROW_DAMAGE);
|
||||
ALIASENCHANTMENTS.put("ad", Enchantment.ARROW_DAMAGE);
|
||||
|
||||
ENCHANTMENTS.put("arrowknockback", Enchantment.ARROW_KNOCKBACK);
|
||||
ALIASENCHANTMENTS.put("arrowkb", Enchantment.ARROW_KNOCKBACK);
|
||||
ENCHANTMENTS.put("punch", Enchantment.ARROW_KNOCKBACK);
|
||||
ALIASENCHANTMENTS.put("arrowpunch", Enchantment.ARROW_KNOCKBACK);
|
||||
ALIASENCHANTMENTS.put("ak", Enchantment.ARROW_KNOCKBACK);
|
||||
|
||||
ALIASENCHANTMENTS.put("infinitearrows", Enchantment.ARROW_INFINITE);
|
||||
ENCHANTMENTS.put("infarrows", Enchantment.ARROW_INFINITE);
|
||||
ENCHANTMENTS.put("infinity", Enchantment.ARROW_INFINITE);
|
||||
ALIASENCHANTMENTS.put("infinite", Enchantment.ARROW_INFINITE);
|
||||
ALIASENCHANTMENTS.put("unlimited", Enchantment.ARROW_INFINITE);
|
||||
ALIASENCHANTMENTS.put("unlimitedarrows", Enchantment.ARROW_INFINITE);
|
||||
ALIASENCHANTMENTS.put("ai", Enchantment.ARROW_INFINITE);
|
||||
|
||||
ENCHANTMENTS.put("luck", Enchantment.LUCK);
|
||||
ALIASENCHANTMENTS.put("luckofsea", Enchantment.LUCK);
|
||||
ALIASENCHANTMENTS.put("luckofseas", Enchantment.LUCK);
|
||||
ALIASENCHANTMENTS.put("rodluck", Enchantment.LUCK);
|
||||
|
||||
ENCHANTMENTS.put("lure", Enchantment.LURE);
|
||||
ALIASENCHANTMENTS.put("rodlure", Enchantment.LURE);
|
||||
|
||||
// 1.8
|
||||
try {
|
||||
final Enchantment depthStrider = Enchantment.getByName("DEPTH_STRIDER");
|
||||
if (depthStrider != null) {
|
||||
ENCHANTMENTS.put("depthstrider", depthStrider);
|
||||
ALIASENCHANTMENTS.put("depth", depthStrider);
|
||||
ALIASENCHANTMENTS.put("strider", depthStrider);
|
||||
}
|
||||
} catch (final IllegalArgumentException ignored) {
|
||||
}
|
||||
|
||||
// 1.9
|
||||
try {
|
||||
final Enchantment frostWalker = Enchantment.getByName("FROST_WALKER");
|
||||
if (frostWalker != null) {
|
||||
ENCHANTMENTS.put("frostwalker", frostWalker);
|
||||
ALIASENCHANTMENTS.put("frost", frostWalker);
|
||||
ALIASENCHANTMENTS.put("walker", frostWalker);
|
||||
}
|
||||
|
||||
final Enchantment mending = Enchantment.getByName("MENDING");
|
||||
if (mending != null) {
|
||||
ENCHANTMENTS.put("mending", mending);
|
||||
}
|
||||
} catch (final IllegalArgumentException ignored) {
|
||||
}
|
||||
|
||||
// 1.11
|
||||
try {
|
||||
final Enchantment bindingCurse = Enchantment.getByName("BINDING_CURSE");
|
||||
if (bindingCurse != null) {
|
||||
ENCHANTMENTS.put("bindingcurse", bindingCurse);
|
||||
ALIASENCHANTMENTS.put("bindcurse", bindingCurse);
|
||||
ALIASENCHANTMENTS.put("binding", bindingCurse);
|
||||
ALIASENCHANTMENTS.put("bind", bindingCurse);
|
||||
}
|
||||
final Enchantment vanishingCurse = Enchantment.getByName("VANISHING_CURSE");
|
||||
if (vanishingCurse != null) {
|
||||
ENCHANTMENTS.put("vanishingcurse", vanishingCurse);
|
||||
ALIASENCHANTMENTS.put("vanishcurse", vanishingCurse);
|
||||
ALIASENCHANTMENTS.put("vanishing", vanishingCurse);
|
||||
ALIASENCHANTMENTS.put("vanish", vanishingCurse);
|
||||
}
|
||||
final Enchantment sweeping = Enchantment.getByName("SWEEPING_EDGE");
|
||||
if (sweeping != null) {
|
||||
ENCHANTMENTS.put("sweepingedge", sweeping);
|
||||
ALIASENCHANTMENTS.put("sweepedge", sweeping);
|
||||
ALIASENCHANTMENTS.put("sweeping", sweeping);
|
||||
}
|
||||
} catch (final IllegalArgumentException ignored) {
|
||||
}
|
||||
|
||||
try { // 1.13
|
||||
final Enchantment loyalty = Enchantment.getByName("LOYALTY");
|
||||
if (loyalty != null) {
|
||||
ENCHANTMENTS.put("loyalty", loyalty);
|
||||
ALIASENCHANTMENTS.put("loyal", loyalty);
|
||||
ALIASENCHANTMENTS.put("return", loyalty);
|
||||
}
|
||||
final Enchantment impaling = Enchantment.getByName("IMPALING");
|
||||
if (impaling != null) {
|
||||
ENCHANTMENTS.put("impaling", impaling);
|
||||
ALIASENCHANTMENTS.put("impale", impaling);
|
||||
ALIASENCHANTMENTS.put("oceandamage", impaling);
|
||||
ALIASENCHANTMENTS.put("oceandmg", impaling);
|
||||
}
|
||||
final Enchantment riptide = Enchantment.getByName("RIPTIDE");
|
||||
if (riptide != null) {
|
||||
ENCHANTMENTS.put("riptide", riptide);
|
||||
ALIASENCHANTMENTS.put("rip", riptide);
|
||||
ALIASENCHANTMENTS.put("tide", riptide);
|
||||
ALIASENCHANTMENTS.put("launch", riptide);
|
||||
}
|
||||
final Enchantment channelling = Enchantment.getByName("CHANNELING");
|
||||
if (channelling != null) {
|
||||
ENCHANTMENTS.put("channelling", channelling);
|
||||
ALIASENCHANTMENTS.put("chanelling", channelling);
|
||||
ALIASENCHANTMENTS.put("channeling", channelling);
|
||||
ALIASENCHANTMENTS.put("chaneling", channelling);
|
||||
ALIASENCHANTMENTS.put("channel", channelling);
|
||||
}
|
||||
} catch (final IllegalArgumentException ignored) {
|
||||
}
|
||||
|
||||
try { // 1.14
|
||||
final Enchantment multishot = Enchantment.getByName("MULTISHOT");
|
||||
if (multishot != null) {
|
||||
ENCHANTMENTS.put("multishot", multishot);
|
||||
ALIASENCHANTMENTS.put("tripleshot", multishot);
|
||||
}
|
||||
final Enchantment quickCharge = Enchantment.getByName("QUICK_CHARGE");
|
||||
if (quickCharge != null) {
|
||||
ENCHANTMENTS.put("quickcharge", quickCharge);
|
||||
ALIASENCHANTMENTS.put("quickdraw", quickCharge);
|
||||
ALIASENCHANTMENTS.put("fastcharge", quickCharge);
|
||||
ALIASENCHANTMENTS.put("fastdraw", quickCharge);
|
||||
}
|
||||
final Enchantment piercing = Enchantment.getByName("PIERCING");
|
||||
if (piercing != null) {
|
||||
ENCHANTMENTS.put("piercing", piercing);
|
||||
}
|
||||
} catch (final IllegalArgumentException ignored) {
|
||||
}
|
||||
|
||||
try { // 1.16
|
||||
final Enchantment soulspeed = Enchantment.getByName("SOUL_SPEED");
|
||||
if (soulspeed != null) {
|
||||
ENCHANTMENTS.put("soulspeed", soulspeed);
|
||||
ALIASENCHANTMENTS.put("soilspeed", soulspeed);
|
||||
ALIASENCHANTMENTS.put("sandspeed", soulspeed);
|
||||
}
|
||||
} catch (final IllegalArgumentException ignored) {
|
||||
}
|
||||
|
||||
try {
|
||||
final Class<?> namespacedKeyClass = Class.forName("org.bukkit.NamespacedKey");
|
||||
final Class<?> enchantmentClass = Class.forName("org.bukkit.enchantments.Enchantment");
|
||||
enchantmentClass.getDeclaredMethod("getByKey", namespacedKeyClass);
|
||||
isFlat = true;
|
||||
} catch (final ClassNotFoundException | NoSuchMethodException e) {
|
||||
isFlat = false;
|
||||
}
|
||||
}
|
||||
|
||||
private Enchantments() {
|
||||
}
|
||||
|
||||
public static Enchantment getByName(final String name) {
|
||||
Enchantment enchantment = null;
|
||||
if (isFlat) { // 1.13+ only
|
||||
enchantment = Enchantment.getByKey(NamespacedKey.minecraft(name.toLowerCase()));
|
||||
}
|
||||
|
||||
if (enchantment == null) {
|
||||
enchantment = Enchantment.getByName(name.toUpperCase());
|
||||
}
|
||||
if (enchantment == null) {
|
||||
enchantment = ENCHANTMENTS.get(name.toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
if (enchantment == null) {
|
||||
enchantment = ALIASENCHANTMENTS.get(name.toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
return enchantment;
|
||||
}
|
||||
|
||||
public static Set<Entry<String, Enchantment>> entrySet() {
|
||||
return ENCHANTMENTS.entrySet();
|
||||
}
|
||||
|
||||
public static Set<String> keySet() {
|
||||
return ENCHANTMENTS.keySet();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,674 +0,0 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import net.ess3.api.InvalidWorldException;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.CoderResult;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class EssentialsConf extends YamlConfiguration {
|
||||
protected static final Logger LOGGER = Logger.getLogger("Essentials");
|
||||
protected static final Charset UTF8 = StandardCharsets.UTF_8;
|
||||
private static final ExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadExecutor();
|
||||
protected final File configFile;
|
||||
private final AtomicInteger pendingDiskWrites = new AtomicInteger(0);
|
||||
private final AtomicBoolean transaction = new AtomicBoolean(false);
|
||||
private final byte[] bytebuffer = new byte[1024];
|
||||
protected String templateName = null;
|
||||
private Class<?> resourceClass = EssentialsConf.class;
|
||||
|
||||
public EssentialsConf(final File configFile) {
|
||||
super();
|
||||
this.configFile = configFile.getAbsoluteFile();
|
||||
}
|
||||
|
||||
public static BigDecimal toBigDecimal(final String input, final BigDecimal def) {
|
||||
if (input == null || input.isEmpty()) {
|
||||
return def;
|
||||
} else {
|
||||
try {
|
||||
return new BigDecimal(input, MathContext.DECIMAL128);
|
||||
} catch (final NumberFormatException | ArithmeticException e) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void load() {
|
||||
if (pendingDiskWrites.get() != 0) {
|
||||
LOGGER.log(Level.INFO, "File {0} not read, because it''s not yet written to disk.", configFile);
|
||||
return;
|
||||
}
|
||||
if (!configFile.getParentFile().exists()) {
|
||||
if (!configFile.getParentFile().mkdirs()) {
|
||||
LOGGER.log(Level.SEVERE, tl("failedToCreateConfig", configFile.toString()));
|
||||
}
|
||||
}
|
||||
// This will delete files where the first character is 0. In most cases they are broken.
|
||||
if (configFile.exists() && configFile.length() != 0) {
|
||||
try {
|
||||
final InputStream input = new FileInputStream(configFile);
|
||||
try {
|
||||
if (input.read() == 0) {
|
||||
input.close();
|
||||
configFile.delete();
|
||||
}
|
||||
} catch (final IOException ex) {
|
||||
LOGGER.log(Level.SEVERE, null, ex);
|
||||
} finally {
|
||||
try {
|
||||
input.close();
|
||||
} catch (final IOException ex) {
|
||||
LOGGER.log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
} catch (final FileNotFoundException ex) {
|
||||
LOGGER.log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (!configFile.exists()) {
|
||||
if (legacyFileExists()) {
|
||||
convertLegacyFile();
|
||||
} else if (altFileExists()) {
|
||||
convertAltFile();
|
||||
} else if (templateName != null) {
|
||||
LOGGER.log(Level.INFO, tl("creatingConfigFromTemplate", configFile.toString()));
|
||||
createFromTemplate();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
try (final FileInputStream inputStream = new FileInputStream(configFile)) {
|
||||
final long startSize = configFile.length();
|
||||
if (startSize > Integer.MAX_VALUE) {
|
||||
throw new InvalidConfigurationException("File too big");
|
||||
}
|
||||
ByteBuffer buffer = ByteBuffer.allocate((int) startSize);
|
||||
int length;
|
||||
while ((length = inputStream.read(bytebuffer)) != -1) {
|
||||
if (length > buffer.remaining()) {
|
||||
final ByteBuffer resize = ByteBuffer.allocate(buffer.capacity() + length - buffer.remaining());
|
||||
final int resizePosition = buffer.position();
|
||||
// Fix builds compiled against Java 9+ breaking on Java 8
|
||||
((Buffer) buffer).rewind();
|
||||
resize.put(buffer);
|
||||
resize.position(resizePosition);
|
||||
buffer = resize;
|
||||
}
|
||||
buffer.put(bytebuffer, 0, length);
|
||||
}
|
||||
((Buffer) buffer).rewind();
|
||||
final CharBuffer data = CharBuffer.allocate(buffer.capacity());
|
||||
CharsetDecoder decoder = UTF8.newDecoder();
|
||||
CoderResult result = decoder.decode(buffer, data, true);
|
||||
if (result.isError()) {
|
||||
((Buffer) buffer).rewind();
|
||||
((Buffer) data).clear();
|
||||
LOGGER.log(Level.INFO, "File " + configFile.getAbsolutePath() + " is not utf-8 encoded, trying " + Charset.defaultCharset().displayName());
|
||||
decoder = Charset.defaultCharset().newDecoder();
|
||||
result = decoder.decode(buffer, data, true);
|
||||
if (result.isError()) {
|
||||
throw new InvalidConfigurationException("Invalid Characters in file " + configFile.getAbsolutePath());
|
||||
} else {
|
||||
decoder.flush(data);
|
||||
}
|
||||
} else {
|
||||
decoder.flush(data);
|
||||
}
|
||||
final int end = data.position();
|
||||
((Buffer) data).rewind();
|
||||
super.loadFromString(data.subSequence(0, end).toString());
|
||||
}
|
||||
} catch (final IOException ex) {
|
||||
LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
|
||||
} catch (final InvalidConfigurationException ex) {
|
||||
final File broken = new File(configFile.getAbsolutePath() + ".broken." + System.currentTimeMillis());
|
||||
configFile.renameTo(broken);
|
||||
LOGGER.log(Level.SEVERE, "The file " + configFile.toString() + " is broken, it has been renamed to " + broken.toString(), ex.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean legacyFileExists() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void convertLegacyFile() {
|
||||
LOGGER.log(Level.SEVERE, "Unable to import legacy config file.");
|
||||
}
|
||||
|
||||
public boolean altFileExists() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void convertAltFile() {
|
||||
LOGGER.log(Level.SEVERE, "Unable to import alt config file.");
|
||||
}
|
||||
|
||||
private void createFromTemplate() {
|
||||
InputStream istr = null;
|
||||
OutputStream ostr = null;
|
||||
try {
|
||||
istr = resourceClass.getResourceAsStream(templateName);
|
||||
if (istr == null) {
|
||||
LOGGER.log(Level.SEVERE, tl("couldNotFindTemplate", templateName));
|
||||
return;
|
||||
}
|
||||
ostr = new FileOutputStream(configFile);
|
||||
final byte[] buffer = new byte[1024];
|
||||
int length = 0;
|
||||
length = istr.read(buffer);
|
||||
while (length > 0) {
|
||||
ostr.write(buffer, 0, length);
|
||||
length = istr.read(buffer);
|
||||
}
|
||||
} catch (final IOException ex) {
|
||||
LOGGER.log(Level.SEVERE, tl("failedToWriteConfig", configFile.toString()), ex);
|
||||
} finally {
|
||||
try {
|
||||
if (istr != null) {
|
||||
istr.close();
|
||||
}
|
||||
} catch (final IOException ex) {
|
||||
Logger.getLogger(EssentialsConf.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
try {
|
||||
if (ostr != null) {
|
||||
ostr.close();
|
||||
}
|
||||
} catch (final IOException ex) {
|
||||
LOGGER.log(Level.SEVERE, tl("failedToCloseConfig", configFile.toString()), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setTemplateName(final String templateName) {
|
||||
this.templateName = templateName;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return configFile;
|
||||
}
|
||||
|
||||
public void setTemplateName(final String templateName, final Class<?> resClass) {
|
||||
this.templateName = templateName;
|
||||
this.resourceClass = resClass;
|
||||
}
|
||||
|
||||
public void startTransaction() {
|
||||
transaction.set(true);
|
||||
}
|
||||
|
||||
public void stopTransaction() {
|
||||
transaction.set(false);
|
||||
save();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
try {
|
||||
save(configFile);
|
||||
} catch (final IOException ex) {
|
||||
LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveWithError() throws IOException {
|
||||
save(configFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void save(final File file) throws IOException {
|
||||
if (!transaction.get()) {
|
||||
delayedSave(file);
|
||||
}
|
||||
}
|
||||
|
||||
//This may be aborted if there are stagnant requests sitting in queue.
|
||||
//This needs fixed to discard outstanding save requests.
|
||||
public synchronized void forceSave() {
|
||||
try {
|
||||
final Future<?> future = delayedSave(configFile);
|
||||
if (future != null) {
|
||||
future.get();
|
||||
}
|
||||
} catch (final InterruptedException | ExecutionException ex) {
|
||||
LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void cleanup() {
|
||||
forceSave();
|
||||
}
|
||||
|
||||
private Future<?> delayedSave(final File file) {
|
||||
if (file == null) {
|
||||
throw new IllegalArgumentException("File cannot be null");
|
||||
}
|
||||
|
||||
final String data = saveToString();
|
||||
|
||||
if (data.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
pendingDiskWrites.incrementAndGet();
|
||||
|
||||
return EXECUTOR_SERVICE.submit(new WriteRunner(configFile, data, pendingDiskWrites));
|
||||
}
|
||||
|
||||
public boolean hasProperty(final String path) {
|
||||
return isSet(path);
|
||||
}
|
||||
|
||||
public Location getLocation(final String path, final Server server) throws InvalidWorldException {
|
||||
final String worldString = (path == null ? "" : path + ".") + "world";
|
||||
final String worldName = getString(worldString);
|
||||
if (worldName == null || worldName.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final World world = server.getWorld(worldName);
|
||||
if (world == null) {
|
||||
throw new InvalidWorldException(worldName);
|
||||
}
|
||||
return new Location(world, getDouble((path == null ? "" : path + ".") + "x", 0), getDouble((path == null ? "" : path + ".") + "y", 0), getDouble((path == null ? "" : path + ".") + "z", 0), (float) getDouble((path == null ? "" : path + ".") + "yaw", 0), (float) getDouble((path == null ? "" : path + ".") + "pitch", 0));
|
||||
}
|
||||
|
||||
public void setProperty(final String path, final Location loc) {
|
||||
set((path == null ? "" : path + ".") + "world", loc.getWorld().getName());
|
||||
set((path == null ? "" : path + ".") + "x", loc.getX());
|
||||
set((path == null ? "" : path + ".") + "y", loc.getY());
|
||||
set((path == null ? "" : path + ".") + "z", loc.getZ());
|
||||
set((path == null ? "" : path + ".") + "yaw", loc.getYaw());
|
||||
set((path == null ? "" : path + ".") + "pitch", loc.getPitch());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItemStack(final String path) {
|
||||
final ItemStack stack = new ItemStack(Material.valueOf(getString(path + ".type", "AIR")), getInt(path + ".amount", 1), (short) getInt(path + ".damage", 0));
|
||||
final ConfigurationSection enchants = getConfigurationSection(path + ".enchant");
|
||||
if (enchants != null) {
|
||||
for (final String enchant : enchants.getKeys(false)) {
|
||||
final Enchantment enchantment = Enchantment.getByName(enchant.toUpperCase(Locale.ENGLISH));
|
||||
if (enchantment == null) {
|
||||
continue;
|
||||
}
|
||||
final int level = getInt(path + ".enchant." + enchant, enchantment.getStartLevel());
|
||||
stack.addUnsafeEnchantment(enchantment, level);
|
||||
}
|
||||
}
|
||||
return stack;
|
||||
/*
|
||||
* ,
|
||||
* (byte)getInt(path + ".data", 0)
|
||||
*/
|
||||
}
|
||||
|
||||
public void setProperty(final String path, final ItemStack stack) {
|
||||
final Map<String, Object> map = new HashMap<>();
|
||||
map.put("type", stack.getType().toString());
|
||||
map.put("amount", stack.getAmount());
|
||||
map.put("damage", stack.getDurability());
|
||||
final Map<Enchantment, Integer> enchantments = stack.getEnchantments();
|
||||
if (!enchantments.isEmpty()) {
|
||||
final Map<String, Integer> enchant = new HashMap<>();
|
||||
for (final Map.Entry<Enchantment, Integer> entry : enchantments.entrySet()) {
|
||||
enchant.put(entry.getKey().getName().toLowerCase(Locale.ENGLISH), entry.getValue());
|
||||
}
|
||||
map.put("enchant", enchant);
|
||||
}
|
||||
// getData().getData() is broken
|
||||
//map.put("data", stack.getDurability());
|
||||
set(path, map);
|
||||
}
|
||||
|
||||
public void setProperty(final String path, final List object) {
|
||||
set(path, new ArrayList(object));
|
||||
}
|
||||
|
||||
public void setProperty(final String path, final Map object) {
|
||||
set(path, new LinkedHashMap(object));
|
||||
}
|
||||
|
||||
public Object getProperty(final String path) {
|
||||
return get(path);
|
||||
}
|
||||
|
||||
public void setProperty(final String path, final BigDecimal bigDecimal) {
|
||||
set(path, bigDecimal.toString());
|
||||
}
|
||||
|
||||
public void setProperty(final String path, final Object object) {
|
||||
set(path, object);
|
||||
}
|
||||
|
||||
public void removeProperty(final String path) {
|
||||
set(path, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object get(final String path) {
|
||||
return super.get(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Object get(final String path, final Object def) {
|
||||
return super.get(path, def);
|
||||
}
|
||||
|
||||
public synchronized BigDecimal getBigDecimal(final String path, final BigDecimal def) {
|
||||
final String input = super.getString(path);
|
||||
return toBigDecimal(input, def);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean getBoolean(final String path) {
|
||||
return super.getBoolean(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean getBoolean(final String path, final boolean def) {
|
||||
return super.getBoolean(path, def);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<Boolean> getBooleanList(final String path) {
|
||||
return super.getBooleanList(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<Byte> getByteList(final String path) {
|
||||
return super.getByteList(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<Character> getCharacterList(final String path) {
|
||||
return super.getCharacterList(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ConfigurationSection getConfigurationSection(final String path) {
|
||||
return super.getConfigurationSection(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized double getDouble(final String path) {
|
||||
return super.getDouble(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized double getDouble(final String path, final double def) {
|
||||
return super.getDouble(path, def);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<Double> getDoubleList(final String path) {
|
||||
return super.getDoubleList(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<Float> getFloatList(final String path) {
|
||||
return super.getFloatList(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int getInt(final String path) {
|
||||
return super.getInt(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int getInt(final String path, final int def) {
|
||||
return super.getInt(path, def);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<Integer> getIntegerList(final String path) {
|
||||
return super.getIntegerList(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized ItemStack getItemStack(final String path, final ItemStack def) {
|
||||
return super.getItemStack(path, def);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Set<String> getKeys(final boolean deep) {
|
||||
return super.getKeys(deep);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<?> getList(final String path) {
|
||||
return super.getList(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<?> getList(final String path, final List<?> def) {
|
||||
return super.getList(path, def);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized long getLong(final String path) {
|
||||
return super.getLong(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized long getLong(final String path, final long def) {
|
||||
return super.getLong(path, def);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<Long> getLongList(final String path) {
|
||||
return super.getLongList(path);
|
||||
}
|
||||
|
||||
public synchronized Map<String, Object> getMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<Map<?, ?>> getMapList(final String path) {
|
||||
return super.getMapList(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized OfflinePlayer getOfflinePlayer(final String path) {
|
||||
return super.getOfflinePlayer(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized OfflinePlayer getOfflinePlayer(final String path, final OfflinePlayer def) {
|
||||
return super.getOfflinePlayer(path, def);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<Short> getShortList(final String path) {
|
||||
return super.getShortList(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String getString(final String path) {
|
||||
return super.getString(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String getString(final String path, final String def) {
|
||||
return super.getString(path, def);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<String> getStringList(final String path) {
|
||||
return super.getStringList(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Map<String, Object> getValues(final boolean deep) {
|
||||
return super.getValues(deep);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Vector getVector(final String path) {
|
||||
return super.getVector(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Vector getVector(final String path, final Vector def) {
|
||||
return super.getVector(path, def);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isBoolean(final String path) {
|
||||
return super.isBoolean(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isConfigurationSection(final String path) {
|
||||
return super.isConfigurationSection(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isDouble(final String path) {
|
||||
return super.isDouble(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isInt(final String path) {
|
||||
return super.isInt(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isItemStack(final String path) {
|
||||
return super.isItemStack(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isList(final String path) {
|
||||
return super.isList(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isLong(final String path) {
|
||||
return super.isLong(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isOfflinePlayer(final String path) {
|
||||
return super.isOfflinePlayer(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isSet(final String path) {
|
||||
return super.isSet(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isString(final String path) {
|
||||
return super.isString(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isVector(final String path) {
|
||||
return super.isVector(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void set(final String path, final Object value) {
|
||||
super.set(path, value);
|
||||
}
|
||||
|
||||
private static final class WriteRunner implements Runnable {
|
||||
private final File configFile;
|
||||
private final String data;
|
||||
private final AtomicInteger pendingDiskWrites;
|
||||
|
||||
private WriteRunner(final File configFile, final String data, final AtomicInteger pendingDiskWrites) {
|
||||
this.configFile = configFile;
|
||||
this.data = data;
|
||||
this.pendingDiskWrites = pendingDiskWrites;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
//long startTime = System.nanoTime();
|
||||
synchronized (configFile) {
|
||||
if (pendingDiskWrites.get() > 1) {
|
||||
// Writes can be skipped, because they are stored in a queue (in the executor).
|
||||
// Only the last is actually written.
|
||||
pendingDiskWrites.decrementAndGet();
|
||||
//LOGGER.log(Level.INFO, configFile + " skipped writing in " + (System.nanoTime() - startTime) + " nsec.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Files.createParentDirs(configFile);
|
||||
|
||||
if (!configFile.exists()) {
|
||||
try {
|
||||
LOGGER.log(Level.INFO, tl("creatingEmptyConfig", configFile.toString()));
|
||||
if (!configFile.createNewFile()) {
|
||||
LOGGER.log(Level.SEVERE, tl("failedToCreateConfig", configFile.toString()));
|
||||
return;
|
||||
}
|
||||
} catch (final IOException ex) {
|
||||
LOGGER.log(Level.SEVERE, tl("failedToCreateConfig", configFile.toString()), ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try (final FileOutputStream fos = new FileOutputStream(configFile)) {
|
||||
try (final OutputStreamWriter writer = new OutputStreamWriter(fos, UTF8)) {
|
||||
writer.write(data);
|
||||
}
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
LOGGER.log(Level.SEVERE, e.getMessage(), e);
|
||||
} finally {
|
||||
//LOGGER.log(Level.INFO, configFile + " written to disk in " + (System.nanoTime() - startTime) + " nsec.");
|
||||
pendingDiskWrites.decrementAndGet();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,946 +0,0 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import com.earth2me.essentials.commands.Commandfireball;
|
||||
import com.earth2me.essentials.textreader.IText;
|
||||
import com.earth2me.essentials.textreader.KeywordReplacer;
|
||||
import com.earth2me.essentials.textreader.TextInput;
|
||||
import com.earth2me.essentials.textreader.TextPager;
|
||||
import com.earth2me.essentials.utils.DateUtil;
|
||||
import com.earth2me.essentials.utils.LocationUtil;
|
||||
import com.earth2me.essentials.utils.MaterialUtil;
|
||||
import com.earth2me.essentials.utils.VersionUtil;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import net.ess3.api.IEssentials;
|
||||
import net.ess3.api.events.AfkStatusChangeEvent;
|
||||
import org.bukkit.BanEntry;
|
||||
import org.bukkit.BanList;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
import org.bukkit.event.player.PlayerBucketEmptyEvent;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
import org.bukkit.event.player.PlayerCommandSendEvent;
|
||||
import org.bukkit.event.player.PlayerEggThrowEvent;
|
||||
import org.bukkit.event.player.PlayerFishEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.bukkit.event.player.PlayerLoginEvent.Result;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class EssentialsPlayerListener implements Listener {
|
||||
private static final Logger LOGGER = Logger.getLogger("Essentials");
|
||||
private final transient IEssentials ess;
|
||||
|
||||
public EssentialsPlayerListener(final IEssentials parent) {
|
||||
this.ess = parent;
|
||||
}
|
||||
|
||||
private static boolean isEntityPickupEvent() {
|
||||
try {
|
||||
Class.forName("org.bukkit.event.entity.EntityPickupItemEvent");
|
||||
return true;
|
||||
} catch (final ClassNotFoundException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isCommandSendEvent() {
|
||||
try {
|
||||
Class.forName("org.bukkit.event.player.PlayerCommandSendEvent");
|
||||
return true;
|
||||
} catch (final ClassNotFoundException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isArrowPickupEvent() {
|
||||
try {
|
||||
Class.forName("org.bukkit.event.player.PlayerPickupArrowEvent");
|
||||
return true;
|
||||
} catch (final ClassNotFoundException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void registerEvents() {
|
||||
ess.getServer().getPluginManager().registerEvents(this, ess);
|
||||
|
||||
if (isArrowPickupEvent()) {
|
||||
ess.getServer().getPluginManager().registerEvents(new ArrowPickupListener(), ess);
|
||||
}
|
||||
|
||||
if (isEntityPickupEvent()) {
|
||||
ess.getServer().getPluginManager().registerEvents(new PickupListener1_12(), ess);
|
||||
} else {
|
||||
ess.getServer().getPluginManager().registerEvents(new PickupListenerPre1_12(), ess);
|
||||
}
|
||||
|
||||
if (isCommandSendEvent()) {
|
||||
ess.getServer().getPluginManager().registerEvents(new CommandSendListener(), ess);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.NORMAL)
|
||||
public void onPlayerRespawn(final PlayerRespawnEvent event) {
|
||||
final User user = ess.getUser(event.getPlayer());
|
||||
updateCompass(user);
|
||||
user.setDisplayNick();
|
||||
|
||||
if (ess.getSettings().isTeleportInvulnerability()) {
|
||||
user.enableInvulnerabilityAfterTeleport();
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onPlayerChat(final AsyncPlayerChatEvent event) {
|
||||
final User user = ess.getUser(event.getPlayer());
|
||||
if (user.isMuted()) {
|
||||
event.setCancelled(true);
|
||||
|
||||
final String dateDiff = user.getMuteTimeout() > 0 ? DateUtil.formatDateDiff(user.getMuteTimeout()) : null;
|
||||
if (dateDiff == null) {
|
||||
user.sendMessage(user.hasMuteReason() ? tl("voiceSilencedReason", user.getMuteReason()) : tl("voiceSilenced"));
|
||||
} else {
|
||||
user.sendMessage(user.hasMuteReason() ? tl("voiceSilencedReasonTime", dateDiff, user.getMuteReason()) : tl("voiceSilencedTime", dateDiff));
|
||||
}
|
||||
|
||||
LOGGER.info(tl("mutedUserSpeaks", user.getName(), event.getMessage()));
|
||||
}
|
||||
try {
|
||||
final Iterator<Player> it = event.getRecipients().iterator();
|
||||
while (it.hasNext()) {
|
||||
final User u = ess.getUser(it.next());
|
||||
if (u.isIgnoredPlayer(user)) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
} catch (final UnsupportedOperationException ex) {
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().log(Level.INFO, "Ignore could not block chat due to custom chat plugin event.", ex);
|
||||
} else {
|
||||
ess.getLogger().info("Ignore could not block chat due to custom chat plugin event.");
|
||||
}
|
||||
}
|
||||
|
||||
user.updateActivityOnInteract(true);
|
||||
user.setDisplayNick();
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
||||
public void onPlayerMove(final PlayerMoveEvent event) {
|
||||
if (event.getFrom().getBlockX() == event.getTo().getBlockX() && event.getFrom().getBlockZ() == event.getTo().getBlockZ() && event.getFrom().getBlockY() == event.getTo().getBlockY()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ess.getSettings().cancelAfkOnMove() && !ess.getSettings().getFreezeAfkPlayers()) {
|
||||
event.getHandlers().unregister(this);
|
||||
|
||||
if (ess.getSettings().isDebug()) {
|
||||
LOGGER.log(Level.INFO, "Unregistering move listener");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
final User user = ess.getUser(event.getPlayer());
|
||||
if (user.isAfk() && ess.getSettings().getFreezeAfkPlayers()) {
|
||||
final Location from = event.getFrom();
|
||||
final Location origTo = event.getTo();
|
||||
final Location to = origTo.clone();
|
||||
if (origTo.getY() >= from.getBlockY() + 1) {
|
||||
user.updateActivityOnMove(true);
|
||||
return;
|
||||
}
|
||||
to.setX(from.getX());
|
||||
to.setY(from.getY());
|
||||
to.setZ(from.getZ());
|
||||
try {
|
||||
event.setTo(LocationUtil.getSafeDestination(to));
|
||||
} catch (final Exception ex) {
|
||||
event.setTo(to);
|
||||
}
|
||||
return;
|
||||
}
|
||||
final Location afk = user.getAfkPosition();
|
||||
if (afk == null || !event.getTo().getWorld().equals(afk.getWorld()) || afk.distanceSquared(event.getTo()) > 9) {
|
||||
user.updateActivityOnMove(true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onPlayerQuit(final PlayerQuitEvent event) {
|
||||
final User user = ess.getUser(event.getPlayer());
|
||||
|
||||
if (hideJoinQuitMessages() || (ess.getSettings().allowSilentJoinQuit() && user.isAuthorized("essentials.silentquit"))) {
|
||||
event.setQuitMessage(null);
|
||||
} else if (ess.getSettings().isCustomQuitMessage() && event.getQuitMessage() != null) {
|
||||
final Player player = event.getPlayer();
|
||||
final String msg = ess.getSettings().getCustomQuitMessage()
|
||||
.replace("{PLAYER}", player.getDisplayName())
|
||||
.replace("{USERNAME}", player.getName())
|
||||
.replace("{ONLINE}", NumberFormat.getInstance().format(ess.getOnlinePlayers().size()))
|
||||
.replace("{UPTIME}", DateUtil.formatDateDiff(ManagementFactory.getRuntimeMXBean().getStartTime()));
|
||||
|
||||
event.setQuitMessage(msg.isEmpty() ? null : msg);
|
||||
}
|
||||
|
||||
user.startTransaction();
|
||||
if (ess.getSettings().removeGodOnDisconnect() && user.isGodModeEnabled()) {
|
||||
user.setGodModeEnabled(false);
|
||||
}
|
||||
if (user.isVanished()) {
|
||||
user.setVanished(false);
|
||||
}
|
||||
user.setLogoutLocation();
|
||||
if (user.isRecipeSee()) {
|
||||
user.getBase().getOpenInventory().getTopInventory().clear();
|
||||
}
|
||||
|
||||
final ArrayList<HumanEntity> viewers = new ArrayList<>(user.getBase().getInventory().getViewers());
|
||||
for (final HumanEntity viewer : viewers) {
|
||||
if (viewer instanceof Player) {
|
||||
final User uviewer = ess.getUser((Player) viewer);
|
||||
if (uviewer.isInvSee()) {
|
||||
uviewer.getBase().closeInventory();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
user.updateActivity(false, AfkStatusChangeEvent.Cause.QUIT);
|
||||
if (!user.isHidden()) {
|
||||
user.setLastLogout(System.currentTimeMillis());
|
||||
}
|
||||
user.stopTransaction();
|
||||
|
||||
user.dispose();
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onPlayerJoin(final PlayerJoinEvent event) {
|
||||
final String joinMessage = event.getJoinMessage();
|
||||
ess.runTaskAsynchronously(() -> delayedJoin(event.getPlayer(), joinMessage));
|
||||
|
||||
if (hideJoinQuitMessages() || ess.getSettings().allowSilentJoinQuit() || ess.getSettings().isCustomJoinMessage()) {
|
||||
event.setJoinMessage(null);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hideJoinQuitMessages() {
|
||||
return ess.getSettings().hasJoinQuitMessagePlayerCount() && ess.getServer().getOnlinePlayers().size() > ess.getSettings().getJoinQuitMessagePlayerCount();
|
||||
}
|
||||
|
||||
public void delayedJoin(final Player player, final String message) {
|
||||
if (!player.isOnline()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ess.getBackup().onPlayerJoin();
|
||||
final User dUser = ess.getUser(player);
|
||||
|
||||
dUser.startTransaction();
|
||||
if (dUser.isNPC()) {
|
||||
dUser.setNPC(false);
|
||||
}
|
||||
|
||||
final long currentTime = System.currentTimeMillis();
|
||||
dUser.checkMuteTimeout(currentTime);
|
||||
dUser.updateActivity(false, AfkStatusChangeEvent.Cause.JOIN);
|
||||
dUser.stopTransaction();
|
||||
|
||||
class DelayJoinTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
final User user = ess.getUser(player);
|
||||
|
||||
if (!user.getBase().isOnline()) {
|
||||
return;
|
||||
}
|
||||
|
||||
user.startTransaction();
|
||||
|
||||
user.setLastAccountName(user.getBase().getName());
|
||||
user.setLastLogin(currentTime);
|
||||
user.setDisplayNick();
|
||||
updateCompass(user);
|
||||
|
||||
if (!ess.getVanishedPlayersNew().isEmpty() && !user.isAuthorized("essentials.vanish.see")) {
|
||||
for (final String p : ess.getVanishedPlayersNew()) {
|
||||
final Player toVanish = ess.getServer().getPlayerExact(p);
|
||||
if (toVanish != null && toVanish.isOnline()) {
|
||||
user.getBase().hidePlayer(toVanish);
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().info("Hiding vanished player: " + p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (user.isAuthorized("essentials.sleepingignored")) {
|
||||
user.getBase().setSleepingIgnored(true);
|
||||
}
|
||||
|
||||
if (ess.getSettings().allowSilentJoinQuit() && (user.isAuthorized("essentials.silentjoin") || user.isAuthorized("essentials.silentjoin.vanish"))) {
|
||||
if (user.isAuthorized("essentials.silentjoin.vanish")) {
|
||||
user.setVanished(true);
|
||||
}
|
||||
} else if (message == null || hideJoinQuitMessages()) {
|
||||
//NOOP
|
||||
} else if (ess.getSettings().isCustomJoinMessage()) {
|
||||
final String msg = ess.getSettings().getCustomJoinMessage()
|
||||
.replace("{PLAYER}", player.getDisplayName()).replace("{USERNAME}", player.getName())
|
||||
.replace("{UNIQUE}", NumberFormat.getInstance().format(ess.getUserMap().getUniqueUsers()))
|
||||
.replace("{ONLINE}", NumberFormat.getInstance().format(ess.getOnlinePlayers().size()))
|
||||
.replace("{UPTIME}", DateUtil.formatDateDiff(ManagementFactory.getRuntimeMXBean().getStartTime()));
|
||||
if (!msg.isEmpty()) {
|
||||
ess.getServer().broadcastMessage(msg);
|
||||
}
|
||||
} else if (ess.getSettings().allowSilentJoinQuit()) {
|
||||
ess.getServer().broadcastMessage(message);
|
||||
}
|
||||
|
||||
final int motdDelay = ess.getSettings().getMotdDelay() / 50;
|
||||
final DelayMotdTask motdTask = new DelayMotdTask(user);
|
||||
if (motdDelay > 0) {
|
||||
ess.scheduleSyncDelayedTask(motdTask, motdDelay);
|
||||
} else {
|
||||
motdTask.run();
|
||||
}
|
||||
|
||||
if (!ess.getSettings().isCommandDisabled("mail") && user.isAuthorized("essentials.mail")) {
|
||||
final List<String> mail = user.getMails();
|
||||
if (mail.isEmpty()) {
|
||||
if (ess.getSettings().isNotifyNoNewMail()) {
|
||||
user.sendMessage(tl("noNewMail")); // Only notify if they want us to.
|
||||
}
|
||||
} else {
|
||||
user.notifyOfMail();
|
||||
}
|
||||
}
|
||||
|
||||
if (user.isAuthorized("essentials.fly.safelogin")) {
|
||||
user.getBase().setFallDistance(0);
|
||||
if (LocationUtil.shouldFly(user.getLocation())) {
|
||||
user.getBase().setAllowFlight(true);
|
||||
user.getBase().setFlying(true);
|
||||
if (ess.getSettings().isSendFlyEnableOnJoin()) {
|
||||
user.getBase().sendMessage(tl("flyMode", tl("enabled"), user.getDisplayName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!user.isAuthorized("essentials.speed")) {
|
||||
user.getBase().setFlySpeed(0.1f);
|
||||
user.getBase().setWalkSpeed(0.2f);
|
||||
}
|
||||
|
||||
if (user.isSocialSpyEnabled() && !user.isAuthorized("essentials.socialspy")) {
|
||||
user.setSocialSpyEnabled(false);
|
||||
ess.getLogger().log(Level.INFO, "Set socialspy to false for {0} because they had it enabled without permission.", user.getName());
|
||||
}
|
||||
|
||||
if (user.isGodModeEnabled() && !user.isAuthorized("essentials.god")) {
|
||||
user.setGodModeEnabled(false);
|
||||
ess.getLogger().log(Level.INFO, "Set god mode to false for {0} because they had it enabled without permission.", user.getName());
|
||||
}
|
||||
|
||||
user.setConfirmingClearCommand(null);
|
||||
user.getConfirmingPayments().clear();
|
||||
|
||||
user.stopTransaction();
|
||||
}
|
||||
|
||||
class DelayMotdTask implements Runnable {
|
||||
private final User user;
|
||||
|
||||
DelayMotdTask(final User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
IText tempInput = null;
|
||||
|
||||
if (!ess.getSettings().isCommandDisabled("motd")) {
|
||||
try {
|
||||
tempInput = new TextInput(user.getSource(), "motd", true, ess);
|
||||
} catch (final IOException ex) {
|
||||
if (ess.getSettings().isDebug()) {
|
||||
LOGGER.log(Level.WARNING, ex.getMessage(), ex);
|
||||
} else {
|
||||
LOGGER.log(Level.WARNING, ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final IText input = tempInput;
|
||||
|
||||
if (input != null && !input.getLines().isEmpty() && user.isAuthorized("essentials.motd")) {
|
||||
final IText output = new KeywordReplacer(input, user.getSource(), ess);
|
||||
final TextPager pager = new TextPager(output, true);
|
||||
pager.showPage("1", null, "motd", user.getSource());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ess.scheduleSyncDelayedTask(new DelayJoinTask());
|
||||
}
|
||||
|
||||
// Makes the compass item ingame always point to the first essentials home. #EasterEgg
|
||||
// EssentialsX: This can now optionally require a permission to enable, if set in the config.
|
||||
private void updateCompass(final User user) {
|
||||
if (ess.getSettings().isCompassTowardsHomePerm() && !user.isAuthorized("essentials.home.compass")) return;
|
||||
|
||||
final Location loc = user.getHome(user.getLocation());
|
||||
if (loc == null) {
|
||||
PaperLib.getBedSpawnLocationAsync(user.getBase(), false).thenAccept(location -> {
|
||||
if (location != null) {
|
||||
user.getBase().setCompassTarget(location);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
user.getBase().setCompassTarget(loc);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onPlayerLoginBanned(final PlayerLoginEvent event) {
|
||||
if (event.getResult() == Result.KICK_BANNED) {
|
||||
BanEntry banEntry = ess.getServer().getBanList(BanList.Type.NAME).getBanEntry(event.getPlayer().getName());
|
||||
if (banEntry != null) {
|
||||
final Date banExpiry = banEntry.getExpiration();
|
||||
if (banExpiry != null) {
|
||||
final String expiry = DateUtil.formatDateDiff(banExpiry.getTime());
|
||||
event.setKickMessage(tl("tempbanJoin", expiry, banEntry.getReason()));
|
||||
} else {
|
||||
event.setKickMessage(tl("banJoin", banEntry.getReason()));
|
||||
}
|
||||
} else {
|
||||
banEntry = ess.getServer().getBanList(BanList.Type.IP).getBanEntry(event.getAddress().getHostAddress());
|
||||
if (banEntry != null) {
|
||||
event.setKickMessage(tl("banIpJoin", banEntry.getReason()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void onPlayerLogin(final PlayerLoginEvent event) {
|
||||
if (event.getResult() == Result.KICK_FULL) {
|
||||
final User kfuser = ess.getUser(event.getPlayer());
|
||||
if (kfuser.isAuthorized("essentials.joinfullserver")) {
|
||||
event.allow();
|
||||
return;
|
||||
}
|
||||
event.disallow(Result.KICK_FULL, tl("serverFull"));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
||||
public void onPlayerTeleport(final PlayerTeleportEvent event) {
|
||||
final boolean backListener = ess.getSettings().registerBackInListener();
|
||||
final boolean teleportInvulnerability = ess.getSettings().isTeleportInvulnerability();
|
||||
if (backListener || teleportInvulnerability) {
|
||||
final Player player = event.getPlayer();
|
||||
if (player.hasMetadata("NPC")) {
|
||||
return;
|
||||
}
|
||||
final User user = ess.getUser(player);
|
||||
//There is TeleportCause.COMMMAND but plugins have to actively pass the cause in on their teleports.
|
||||
if (user.isAuthorized("essentials.back.onteleport") && backListener && (event.getCause() == TeleportCause.PLUGIN || event.getCause() == TeleportCause.COMMAND)) {
|
||||
user.setLastLocation();
|
||||
}
|
||||
if (teleportInvulnerability && (event.getCause() == TeleportCause.PLUGIN || event.getCause() == TeleportCause.COMMAND)) {
|
||||
user.enableInvulnerabilityAfterTeleport();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
||||
public void onPlayerEggThrow(final PlayerEggThrowEvent event) {
|
||||
final User user = ess.getUser(event.getPlayer());
|
||||
final ItemStack stack = new ItemStack(Material.EGG, 1);
|
||||
if (user.hasUnlimited(stack)) {
|
||||
user.getBase().getInventory().addItem(stack);
|
||||
user.getBase().updateInventory();
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
||||
public void onPlayerBucketEmpty(final PlayerBucketEmptyEvent event) {
|
||||
final User user = ess.getUser(event.getPlayer());
|
||||
if (user.hasUnlimited(new ItemStack(event.getBucket()))) {
|
||||
event.getItemStack().setType(event.getBucket());
|
||||
ess.scheduleSyncDelayedTask(user.getBase()::updateInventory);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onPlayerCommandPreprocess(final PlayerCommandPreprocessEvent event) {
|
||||
final Player player = event.getPlayer();
|
||||
final String cmd = event.getMessage().toLowerCase(Locale.ENGLISH).split(" ")[0].replace("/", "").toLowerCase(Locale.ENGLISH);
|
||||
|
||||
final PluginCommand pluginCommand = ess.getServer().getPluginCommand(cmd);
|
||||
|
||||
if (ess.getSettings().getSocialSpyCommands().contains(cmd) || ess.getSettings().getSocialSpyCommands().contains("*")) {
|
||||
if (pluginCommand == null
|
||||
|| (!pluginCommand.getName().equals("msg") && !pluginCommand.getName().equals("r"))) { // /msg and /r are handled in SimpleMessageRecipient
|
||||
final User user = ess.getUser(player);
|
||||
if (!user.isAuthorized("essentials.chat.spy.exempt")) {
|
||||
for (final User spyer : ess.getOnlineUsers()) {
|
||||
if (spyer.isSocialSpyEnabled() && !player.equals(spyer.getBase())) {
|
||||
if (user.isMuted() && ess.getSettings().getSocialSpyListenMutedPlayers()) {
|
||||
spyer.sendMessage(tl("socialSpyMutedPrefix") + player.getDisplayName() + ": " + event.getMessage());
|
||||
} else {
|
||||
spyer.sendMessage(tl("socialSpyPrefix") + player.getDisplayName() + ": " + event.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final User user = ess.getUser(player);
|
||||
if (user.isMuted() && (ess.getSettings().getMuteCommands().contains(cmd) || ess.getSettings().getMuteCommands().contains("*"))) {
|
||||
event.setCancelled(true);
|
||||
final String dateDiff = user.getMuteTimeout() > 0 ? DateUtil.formatDateDiff(user.getMuteTimeout()) : null;
|
||||
if (dateDiff == null) {
|
||||
player.sendMessage(user.hasMuteReason() ? tl("voiceSilencedReason", user.getMuteReason()) : tl("voiceSilenced"));
|
||||
} else {
|
||||
player.sendMessage(user.hasMuteReason() ? tl("voiceSilencedReasonTime", dateDiff, user.getMuteReason()) : tl("voiceSilencedTime", dateDiff));
|
||||
}
|
||||
LOGGER.info(tl("mutedUserSpeaks", player.getName(), event.getMessage()));
|
||||
return;
|
||||
}
|
||||
|
||||
boolean broadcast = true; // whether to broadcast the updated activity
|
||||
boolean update = true; // Only modified when the command is afk
|
||||
|
||||
if (pluginCommand != null) {
|
||||
// Switch case for commands that shouldn't broadcast afk activity.
|
||||
switch (pluginCommand.getName()) {
|
||||
case "afk":
|
||||
update = false;
|
||||
// fall through
|
||||
case "vanish":
|
||||
broadcast = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (update) {
|
||||
user.updateActivityOnInteract(broadcast);
|
||||
}
|
||||
|
||||
if (ess.getSettings().isCommandCooldownsEnabled() && pluginCommand != null
|
||||
&& !user.isAuthorized("essentials.commandcooldowns.bypass")) {
|
||||
final int argStartIndex = event.getMessage().indexOf(" ");
|
||||
final String args = argStartIndex == -1 ? "" // No arguments present
|
||||
: " " + event.getMessage().substring(argStartIndex); // arguments start at argStartIndex; substring from there.
|
||||
final String fullCommand = pluginCommand.getName() + args;
|
||||
|
||||
// Used to determine whether a user already has an existing cooldown
|
||||
// If so, no need to check for (and write) new ones.
|
||||
boolean cooldownFound = false;
|
||||
|
||||
// Iterate over a copy of getCommandCooldowns in case of concurrent modifications
|
||||
for (final Entry<Pattern, Long> entry : new HashMap<>(user.getCommandCooldowns()).entrySet()) {
|
||||
// Remove any expired cooldowns
|
||||
if (entry.getValue() <= System.currentTimeMillis()) {
|
||||
user.clearCommandCooldown(entry.getKey());
|
||||
// Don't break in case there are other command cooldowns left to clear.
|
||||
} else if (entry.getKey().matcher(fullCommand).matches()) {
|
||||
// User's current cooldown hasn't expired, inform and terminate cooldown code.
|
||||
if (entry.getValue() > System.currentTimeMillis()) {
|
||||
final String commandCooldownTime = DateUtil.formatDateDiff(entry.getValue());
|
||||
user.sendMessage(tl("commandCooldown", commandCooldownTime));
|
||||
cooldownFound = true;
|
||||
event.setCancelled(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!cooldownFound) {
|
||||
final Entry<Pattern, Long> cooldownEntry = ess.getSettings().getCommandCooldownEntry(fullCommand);
|
||||
|
||||
if (cooldownEntry != null) {
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().info("Applying " + cooldownEntry.getValue() + "ms cooldown on /" + fullCommand + " for" + user.getName() + ".");
|
||||
}
|
||||
final Date expiry = new Date(System.currentTimeMillis() + cooldownEntry.getValue());
|
||||
user.addCommandCooldown(cooldownEntry.getKey(), expiry, ess.getSettings().isCommandCooldownPersistent(fullCommand));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.NORMAL)
|
||||
public void onPlayerChangedWorldFlyReset(final PlayerChangedWorldEvent event) {
|
||||
final User user = ess.getUser(event.getPlayer());
|
||||
|
||||
if (ess.getSettings().isWorldChangeFlyResetEnabled()) {
|
||||
if (user.getBase().getGameMode() != GameMode.CREATIVE
|
||||
// COMPAT: String compare for 1.7.10
|
||||
&& !user.getBase().getGameMode().name().equals("SPECTATOR")
|
||||
&& !user.isAuthorized("essentials.fly")) {
|
||||
user.getBase().setFallDistance(0f);
|
||||
user.getBase().setAllowFlight(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (ess.getSettings().isWorldChangeSpeedResetEnabled()) {
|
||||
if (!user.isAuthorized("essentials.speed")) {
|
||||
user.getBase().setFlySpeed(0.1f);
|
||||
user.getBase().setWalkSpeed(0.2f);
|
||||
} else {
|
||||
if (user.getBase().getFlySpeed() > ess.getSettings().getMaxFlySpeed() && !user.isAuthorized("essentials.speed.bypass")) {
|
||||
user.getBase().setFlySpeed((float) ess.getSettings().getMaxFlySpeed());
|
||||
} else {
|
||||
user.getBase().setFlySpeed(user.getBase().getFlySpeed() * 0.99999f);
|
||||
}
|
||||
|
||||
if (user.getBase().getWalkSpeed() > ess.getSettings().getMaxWalkSpeed() && !user.isAuthorized("essentials.speed.bypass")) {
|
||||
user.getBase().setWalkSpeed((float) ess.getSettings().getMaxWalkSpeed());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onPlayerChangedWorld(final PlayerChangedWorldEvent event) {
|
||||
final User user = ess.getUser(event.getPlayer());
|
||||
final String newWorld = event.getPlayer().getLocation().getWorld().getName();
|
||||
user.setDisplayNick();
|
||||
updateCompass(user);
|
||||
if (ess.getSettings().getNoGodWorlds().contains(newWorld) && user.isGodModeEnabledRaw()) {
|
||||
// Player god mode is never disabled in order to retain it when changing worlds once more.
|
||||
// With that said, players will still take damage as per the result of User#isGodModeEnabled()
|
||||
user.sendMessage(tl("noGodWorldWarning"));
|
||||
}
|
||||
|
||||
if (!user.getWorld().getName().equals(newWorld)) {
|
||||
user.sendMessage(tl("currentWorld", newWorld));
|
||||
}
|
||||
if (user.isVanished()) {
|
||||
user.setVanished(user.isAuthorized("essentials.vanish"));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.NORMAL)
|
||||
public void onPlayerInteract(final PlayerInteractEvent event) {
|
||||
boolean updateActivity = true;
|
||||
|
||||
switch (event.getAction()) {
|
||||
case RIGHT_CLICK_BLOCK:
|
||||
if (!event.isCancelled() && MaterialUtil.isBed(event.getClickedBlock().getType()) && ess.getSettings().getUpdateBedAtDaytime()) {
|
||||
final User player = ess.getUser(event.getPlayer());
|
||||
if (player.isAuthorized("essentials.sethome.bed") && player.getWorld().getEnvironment().equals(World.Environment.NORMAL)) {
|
||||
player.getBase().setBedSpawnLocation(event.getClickedBlock().getLocation());
|
||||
// In 1.15 and above, vanilla sends its own bed spawn message.
|
||||
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_15_R01)) {
|
||||
player.sendMessage(tl("bedSet", player.getLocation().getWorld().getName(), player.getLocation().getBlockX(), player.getLocation().getBlockY(), player.getLocation().getBlockZ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LEFT_CLICK_AIR:
|
||||
if (event.getPlayer().isFlying()) {
|
||||
final User user = ess.getUser(event.getPlayer());
|
||||
if (user.isFlyClickJump()) {
|
||||
useFlyClickJump(user);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// fall through
|
||||
case LEFT_CLICK_BLOCK:
|
||||
if (event.getItem() != null && event.getItem().getType() != Material.AIR) {
|
||||
final User user = ess.getUser(event.getPlayer());
|
||||
if (user.hasPowerTools() && user.arePowerToolsEnabled() && usePowertools(user, event.getItem().getType())) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PHYSICAL:
|
||||
updateActivity = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (updateActivity) {
|
||||
ess.getUser(event.getPlayer()).updateActivityOnInteract(true);
|
||||
}
|
||||
}
|
||||
|
||||
// This method allows the /jump lock feature to work, allows teleporting while flying #EasterEgg
|
||||
private void useFlyClickJump(final User user) {
|
||||
try {
|
||||
final Location otarget = LocationUtil.getTarget(user.getBase());
|
||||
|
||||
class DelayedClickJumpTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
final Location loc = user.getLocation();
|
||||
loc.setX(otarget.getX());
|
||||
loc.setZ(otarget.getZ());
|
||||
while (LocationUtil.isBlockDamaging(loc.getWorld(), loc.getBlockX(), loc.getBlockY() - 1, loc.getBlockZ())) {
|
||||
loc.setY(loc.getY() + 1d);
|
||||
}
|
||||
PaperLib.teleportAsync(user.getBase(), loc, TeleportCause.PLUGIN);
|
||||
}
|
||||
}
|
||||
|
||||
ess.scheduleSyncDelayedTask(new DelayedClickJumpTask());
|
||||
} catch (final Exception ex) {
|
||||
if (ess.getSettings().isDebug()) {
|
||||
LOGGER.log(Level.WARNING, ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean usePowertools(final User user, final Material material) {
|
||||
final List<String> commandList = user.getPowertool(material);
|
||||
if (commandList == null || commandList.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
boolean used = false;
|
||||
// We need to loop through each command and execute
|
||||
for (final String command : commandList) {
|
||||
if (command.contains("{player}")) {
|
||||
} else if (command.startsWith("c:")) {
|
||||
used = true;
|
||||
user.getBase().chat(command.substring(2));
|
||||
} else {
|
||||
used = true;
|
||||
|
||||
class PowerToolUseTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
user.getBase().chat("/" + command);
|
||||
LOGGER.log(Level.INFO, String.format("[PT] %s issued server command: /%s", user.getName(), command));
|
||||
}
|
||||
}
|
||||
|
||||
ess.scheduleSyncDelayedTask(new PowerToolUseTask());
|
||||
|
||||
}
|
||||
}
|
||||
return used;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
|
||||
public void onInventoryClickEvent(final InventoryClickEvent event) {
|
||||
Player refreshPlayer = null;
|
||||
final Inventory top = event.getView().getTopInventory();
|
||||
final InventoryType type = top.getType();
|
||||
|
||||
final Inventory clickedInventory;
|
||||
if (event.getRawSlot() < 0) {
|
||||
clickedInventory = null;
|
||||
} else {
|
||||
clickedInventory = event.getRawSlot() < top.getSize() ? top : event.getView().getBottomInventory();
|
||||
}
|
||||
|
||||
if (type == InventoryType.PLAYER) {
|
||||
final User user = ess.getUser((Player) event.getWhoClicked());
|
||||
final InventoryHolder invHolder = top.getHolder();
|
||||
if (invHolder instanceof HumanEntity) {
|
||||
final User invOwner = ess.getUser((Player) invHolder);
|
||||
if (user.isInvSee() && (!user.isAuthorized("essentials.invsee.modify") || invOwner.isAuthorized("essentials.invsee.preventmodify") || !invOwner.getBase().isOnline())) {
|
||||
event.setCancelled(true);
|
||||
refreshPlayer = user.getBase();
|
||||
}
|
||||
}
|
||||
} else if (type == InventoryType.ENDER_CHEST) {
|
||||
final User user = ess.getUser((Player) event.getWhoClicked());
|
||||
if (user.isEnderSee() && !user.isAuthorized("essentials.enderchest.modify")) {
|
||||
event.setCancelled(true);
|
||||
refreshPlayer = user.getBase();
|
||||
}
|
||||
} else if (type == InventoryType.WORKBENCH) {
|
||||
final User user = ess.getUser((Player) event.getWhoClicked());
|
||||
if (user.isRecipeSee()) {
|
||||
event.setCancelled(true);
|
||||
refreshPlayer = user.getBase();
|
||||
}
|
||||
} else if (type == InventoryType.CHEST && top.getSize() == 9) {
|
||||
final User user = ess.getUser((Player) event.getWhoClicked());
|
||||
final InventoryHolder invHolder = top.getHolder();
|
||||
if (invHolder instanceof HumanEntity && user.isInvSee()) {
|
||||
event.setCancelled(true);
|
||||
refreshPlayer = user.getBase();
|
||||
}
|
||||
} else if (clickedInventory != null && clickedInventory.getType() == InventoryType.PLAYER) {
|
||||
if (ess.getSettings().isDirectHatAllowed() && event.getClick() == ClickType.LEFT && event.getSlot() == 39
|
||||
&& event.getCursor().getType() != Material.AIR && event.getCursor().getType().getMaxDurability() == 0
|
||||
&& !MaterialUtil.isSkull(event.getCursor().getType())
|
||||
&& ess.getUser(event.getWhoClicked()).isAuthorized("essentials.hat") && !ess.getUser(event.getWhoClicked()).isAuthorized("essentials.hat.prevent-type." + event.getCursor().getType().name().toLowerCase())) {
|
||||
event.setCancelled(true);
|
||||
final PlayerInventory inv = (PlayerInventory) clickedInventory;
|
||||
final ItemStack head = inv.getHelmet();
|
||||
inv.setHelmet(event.getCursor());
|
||||
event.setCursor(head);
|
||||
}
|
||||
}
|
||||
|
||||
if (refreshPlayer != null) {
|
||||
ess.scheduleSyncDelayedTask(refreshPlayer::updateInventory, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onInventoryCloseEvent(final InventoryCloseEvent event) {
|
||||
Player refreshPlayer = null;
|
||||
final Inventory top = event.getView().getTopInventory();
|
||||
final InventoryType type = top.getType();
|
||||
if (type == InventoryType.PLAYER) {
|
||||
final User user = ess.getUser((Player) event.getPlayer());
|
||||
user.setInvSee(false);
|
||||
refreshPlayer = user.getBase();
|
||||
} else if (type == InventoryType.ENDER_CHEST) {
|
||||
final User user = ess.getUser((Player) event.getPlayer());
|
||||
user.setEnderSee(false);
|
||||
refreshPlayer = user.getBase();
|
||||
} else if (type == InventoryType.WORKBENCH) {
|
||||
final User user = ess.getUser((Player) event.getPlayer());
|
||||
if (user.isRecipeSee()) {
|
||||
user.setRecipeSee(false);
|
||||
event.getView().getTopInventory().clear();
|
||||
refreshPlayer = user.getBase();
|
||||
}
|
||||
} else if (type == InventoryType.CHEST && top.getSize() == 9) {
|
||||
final InventoryHolder invHolder = top.getHolder();
|
||||
if (invHolder instanceof HumanEntity) {
|
||||
final User user = ess.getUser((Player) event.getPlayer());
|
||||
user.setInvSee(false);
|
||||
refreshPlayer = user.getBase();
|
||||
}
|
||||
}
|
||||
|
||||
if (refreshPlayer != null) {
|
||||
ess.scheduleSyncDelayedTask(refreshPlayer::updateInventory, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
public void onPlayerFishEvent(final PlayerFishEvent event) {
|
||||
final User user = ess.getUser(event.getPlayer());
|
||||
user.updateActivityOnInteract(true);
|
||||
}
|
||||
|
||||
private static final class ArrowPickupListener implements Listener {
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onArrowPickup(final org.bukkit.event.player.PlayerPickupArrowEvent event) {
|
||||
if (event.getItem().hasMetadata(Commandfireball.FIREBALL_META_KEY)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class PickupListenerPre1_12 implements Listener {
|
||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
public void onPlayerPickupItem(final org.bukkit.event.player.PlayerPickupItemEvent event) {
|
||||
if (event.getItem().hasMetadata(Commandfireball.FIREBALL_META_KEY)) {
|
||||
event.setCancelled(true);
|
||||
} else if (ess.getSettings().getDisableItemPickupWhileAfk()) {
|
||||
if (ess.getUser(event.getPlayer()).isAfk()) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class PickupListener1_12 implements Listener {
|
||||
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
|
||||
public void onPlayerPickupItem(final org.bukkit.event.entity.EntityPickupItemEvent event) {
|
||||
if (ess.getSettings().getDisableItemPickupWhileAfk() && event.getEntity() instanceof Player) {
|
||||
if (ess.getUser((Player) event.getEntity()).isAfk()) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class CommandSendListener implements Listener {
|
||||
@EventHandler(priority = EventPriority.NORMAL)
|
||||
public void onCommandSend(final PlayerCommandSendEvent event) {
|
||||
final User user = ess.getUser(event.getPlayer());
|
||||
|
||||
final Set<PluginCommand> checked = new HashSet<>();
|
||||
final Set<PluginCommand> toRemove = new HashSet<>();
|
||||
|
||||
event.getCommands().removeIf(label -> {
|
||||
if (isEssentialsCommand(label)) {
|
||||
final PluginCommand command = ess.getServer().getPluginCommand(label);
|
||||
if (!checked.contains(command)) {
|
||||
checked.add(command);
|
||||
if (!user.isAuthorized(command.getName().equals("r") ? "essentials.msg" : "essentials." + command.getName())) {
|
||||
toRemove.add(command);
|
||||
}
|
||||
}
|
||||
return toRemove.contains(command);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().info("Removed commands: " + toRemove.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if all of the following are true:
|
||||
* - The command is a plugin command
|
||||
* - The plugin command is from a plugin in an essentials-controlled package
|
||||
* - There is no known alternative OR the alternative is overridden by Essentials
|
||||
*/
|
||||
private boolean isEssentialsCommand(final String label) {
|
||||
final PluginCommand command = ess.getServer().getPluginCommand(label);
|
||||
|
||||
return command != null
|
||||
&& (command.getPlugin() == ess || command.getPlugin().getClass().getName().startsWith("com.earth2me.essentials"))
|
||||
&& (ess.getSettings().isCommandOverridden(label) || (ess.getAlternativeCommandsHandler().getAlternative(label) == null));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,781 +0,0 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import com.earth2me.essentials.craftbukkit.BanLookup;
|
||||
import com.earth2me.essentials.craftbukkit.FakeWorld;
|
||||
import com.earth2me.essentials.settings.Spawns;
|
||||
import com.earth2me.essentials.storage.YamlStorageWriter;
|
||||
import com.earth2me.essentials.utils.StringUtil;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.Maps;
|
||||
import net.ess3.api.IEssentials;
|
||||
import org.bukkit.BanList;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.math.BigInteger;
|
||||
import java.security.DigestInputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class EssentialsUpgrade {
|
||||
private final static Logger LOGGER = Logger.getLogger("Essentials");
|
||||
private static final FileFilter YML_FILTER = pathname -> pathname.isFile() && pathname.getName().endsWith(".yml");
|
||||
private static final String PATTERN_CONFIG_UUID_REGEX = "(?mi)^uuid:\\s*([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\\s*$";
|
||||
private static final Pattern PATTERN_CONFIG_UUID = Pattern.compile(PATTERN_CONFIG_UUID_REGEX);
|
||||
private static final String PATTERN_CONFIG_NAME_REGEX = "(?mi)^lastAccountName:\\s*[\"\']?(\\w+)[\"\']?\\s*$";
|
||||
private static final Pattern PATTERN_CONFIG_NAME = Pattern.compile(PATTERN_CONFIG_NAME_REGEX);
|
||||
private final transient IEssentials ess;
|
||||
private final transient EssentialsConf doneFile;
|
||||
|
||||
EssentialsUpgrade(final IEssentials essentials) {
|
||||
ess = essentials;
|
||||
if (!ess.getDataFolder().exists()) {
|
||||
ess.getDataFolder().mkdirs();
|
||||
}
|
||||
doneFile = new EssentialsConf(new File(ess.getDataFolder(), "upgrades-done.yml"));
|
||||
doneFile.load();
|
||||
}
|
||||
|
||||
public static void uuidFileConvert(final IEssentials ess, final Boolean ignoreUFCache) {
|
||||
ess.getLogger().info("Starting Essentials UUID userdata conversion");
|
||||
|
||||
final File userdir = new File(ess.getDataFolder(), "userdata");
|
||||
if (!userdir.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int countFiles = 0;
|
||||
int countFails = 0;
|
||||
int countEssCache = 0;
|
||||
int countBukkit = 0;
|
||||
|
||||
ess.getLogger().info("Found " + userdir.list().length + " files to convert...");
|
||||
|
||||
for (final String string : userdir.list()) {
|
||||
if (!string.endsWith(".yml") || string.length() < 5) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final int showProgress = countFiles % 250;
|
||||
|
||||
if (showProgress == 0) {
|
||||
ess.getUserMap().getUUIDMap().forceWriteUUIDMap();
|
||||
ess.getLogger().info("Converted " + countFiles + "/" + userdir.list().length);
|
||||
}
|
||||
|
||||
countFiles++;
|
||||
|
||||
final String name = string.substring(0, string.length() - 4);
|
||||
final EssentialsUserConf config;
|
||||
UUID uuid = null;
|
||||
try {
|
||||
uuid = UUID.fromString(name);
|
||||
} catch (final IllegalArgumentException ex) {
|
||||
final File file = new File(userdir, string);
|
||||
final EssentialsConf conf = new EssentialsConf(file);
|
||||
conf.load();
|
||||
conf.setProperty("lastAccountName", name);
|
||||
conf.save();
|
||||
|
||||
final String uuidConf = ignoreUFCache ? "force-uuid" : "uuid";
|
||||
|
||||
final String uuidString = conf.getString(uuidConf, null);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
try {
|
||||
uuid = UUID.fromString(uuidString);
|
||||
countEssCache++;
|
||||
break;
|
||||
} catch (final Exception ex2) {
|
||||
if (conf.getBoolean("npc", false)) {
|
||||
uuid = UUID.nameUUIDFromBytes(("NPC:" + name).getBytes(Charsets.UTF_8));
|
||||
break;
|
||||
}
|
||||
|
||||
final org.bukkit.OfflinePlayer player = ess.getServer().getOfflinePlayer(name);
|
||||
uuid = player.getUniqueId();
|
||||
}
|
||||
|
||||
if (uuid != null) {
|
||||
countBukkit++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (uuid != null) {
|
||||
conf.forceSave();
|
||||
config = new EssentialsUserConf(name, uuid, new File(userdir, uuid + ".yml"));
|
||||
config.convertLegacyFile();
|
||||
ess.getUserMap().trackUUID(uuid, name, false);
|
||||
continue;
|
||||
}
|
||||
countFails++;
|
||||
}
|
||||
}
|
||||
ess.getUserMap().getUUIDMap().forceWriteUUIDMap();
|
||||
|
||||
ess.getLogger().info("Converted " + countFiles + "/" + countFiles + ". Conversion complete.");
|
||||
ess.getLogger().info("Converted via cache: " + countEssCache + " :: Converted via lookup: " + countBukkit + " :: Failed to convert: " + countFails);
|
||||
ess.getLogger().info("To rerun the conversion type /essentials uuidconvert");
|
||||
}
|
||||
|
||||
public void convertIgnoreList() {
|
||||
final Pattern pattern = Pattern.compile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$");
|
||||
if (doneFile.getBoolean("updateUsersIgnoreListUUID", false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.info("Attempting to migrate ignore list to UUIDs");
|
||||
|
||||
final File userdataFolder = new File(ess.getDataFolder(), "userdata");
|
||||
if (!userdataFolder.exists() || !userdataFolder.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
final File[] userFiles = userdataFolder.listFiles();
|
||||
|
||||
for (final File file : userFiles) {
|
||||
if (!file.isFile() || !file.getName().endsWith(".yml")) {
|
||||
continue;
|
||||
}
|
||||
final EssentialsConf config = new EssentialsConf(file);
|
||||
try {
|
||||
config.load();
|
||||
if (config.hasProperty("ignore")) {
|
||||
final List<String> migratedIgnores = new ArrayList<>();
|
||||
for (final String name : Collections.synchronizedList(config.getStringList("ignore"))) {
|
||||
if (name == null) {
|
||||
continue;
|
||||
}
|
||||
if (pattern.matcher(name.trim()).matches()) {
|
||||
LOGGER.info("Detected already migrated ignore list!");
|
||||
return;
|
||||
}
|
||||
final User user = ess.getOfflineUser(name);
|
||||
if (user != null && user.getBase() != null) {
|
||||
migratedIgnores.add(user.getBase().getUniqueId().toString());
|
||||
}
|
||||
}
|
||||
config.removeProperty("ignore");
|
||||
config.setProperty("ignore", migratedIgnores);
|
||||
config.forceSave();
|
||||
}
|
||||
} catch (final RuntimeException ex) {
|
||||
LOGGER.log(Level.INFO, "File: " + file.toString());
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
doneFile.setProperty("updateUsersIgnoreListUUID", true);
|
||||
doneFile.save();
|
||||
LOGGER.info("Done converting ignore list.");
|
||||
}
|
||||
|
||||
public void convertKits() {
|
||||
final Kits kits = ess.getKits();
|
||||
final EssentialsConf config = kits.getConfig();
|
||||
if (doneFile.getBoolean("kitsyml", false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.info("Attempting to convert old kits in config.yml to new kits.yml");
|
||||
|
||||
final ConfigurationSection section = ess.getSettings().getKitSection();
|
||||
if (section == null) {
|
||||
LOGGER.info("No kits found to migrate.");
|
||||
return;
|
||||
}
|
||||
|
||||
final Map<String, Object> legacyKits = ess.getSettings().getKitSection().getValues(true);
|
||||
|
||||
for (final Map.Entry<String, Object> entry : legacyKits.entrySet()) {
|
||||
LOGGER.info("Converting " + entry.getKey());
|
||||
config.set("kits." + entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
config.save();
|
||||
doneFile.setProperty("kitsyml", true);
|
||||
doneFile.save();
|
||||
LOGGER.info("Done converting kits.");
|
||||
}
|
||||
|
||||
private void moveMotdRulesToFile(final String name) {
|
||||
if (doneFile.getBoolean("move" + name + "ToFile", false)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final File file = new File(ess.getDataFolder(), name + ".txt");
|
||||
if (file.exists()) {
|
||||
return;
|
||||
}
|
||||
final File configFile = new File(ess.getDataFolder(), "config.yml");
|
||||
if (!configFile.exists()) {
|
||||
return;
|
||||
}
|
||||
final EssentialsConf conf = new EssentialsConf(configFile);
|
||||
conf.load();
|
||||
final List<String> lines = conf.getStringList(name);
|
||||
if (lines != null && !lines.isEmpty()) {
|
||||
if (!file.createNewFile()) {
|
||||
throw new IOException("Failed to create file " + file);
|
||||
}
|
||||
final PrintWriter writer = new PrintWriter(file);
|
||||
|
||||
for (final String line : lines) {
|
||||
writer.println(line);
|
||||
}
|
||||
writer.close();
|
||||
}
|
||||
doneFile.setProperty("move" + name + "ToFile", true);
|
||||
doneFile.save();
|
||||
} catch (final IOException e) {
|
||||
LOGGER.log(Level.SEVERE, tl("upgradingFilesError"), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeLinesFromConfig(final File file, final String regex, final String info) throws Exception {
|
||||
boolean needUpdate = false;
|
||||
final BufferedReader bReader = new BufferedReader(new FileReader(file));
|
||||
final File tempFile = File.createTempFile("essentialsupgrade", ".tmp.yml", ess.getDataFolder());
|
||||
final BufferedWriter bWriter = new BufferedWriter(new FileWriter(tempFile));
|
||||
do {
|
||||
final String line = bReader.readLine();
|
||||
if (line == null) {
|
||||
break;
|
||||
}
|
||||
if (line.matches(regex)) {
|
||||
if (!needUpdate && info != null) {
|
||||
bWriter.write(info, 0, info.length());
|
||||
bWriter.newLine();
|
||||
}
|
||||
needUpdate = true;
|
||||
} else {
|
||||
if (line.endsWith("\r\n")) {
|
||||
bWriter.write(line, 0, line.length() - 2);
|
||||
} else if (line.endsWith("\r") || line.endsWith("\n")) {
|
||||
bWriter.write(line, 0, line.length() - 1);
|
||||
} else {
|
||||
bWriter.write(line, 0, line.length());
|
||||
}
|
||||
bWriter.newLine();
|
||||
}
|
||||
} while (true);
|
||||
bReader.close();
|
||||
bWriter.close();
|
||||
if (needUpdate) {
|
||||
if (!file.renameTo(new File(file.getParentFile(), file.getName().concat("." + System.currentTimeMillis() + ".upgradebackup")))) {
|
||||
throw new Exception(tl("configFileMoveError"));
|
||||
}
|
||||
if (!tempFile.renameTo(file)) {
|
||||
throw new Exception(tl("configFileRenameError"));
|
||||
}
|
||||
} else {
|
||||
tempFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUsersPowerToolsFormat() {
|
||||
if (doneFile.getBoolean("updateUsersPowerToolsFormat", false)) {
|
||||
return;
|
||||
}
|
||||
final File userdataFolder = new File(ess.getDataFolder(), "userdata");
|
||||
if (!userdataFolder.exists() || !userdataFolder.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
final File[] userFiles = userdataFolder.listFiles();
|
||||
|
||||
for (final File file : userFiles) {
|
||||
if (!file.isFile() || !file.getName().endsWith(".yml")) {
|
||||
continue;
|
||||
}
|
||||
final EssentialsConf config = new EssentialsConf(file);
|
||||
try {
|
||||
config.load();
|
||||
if (config.hasProperty("powertools")) {
|
||||
final Map<String, Object> powertools = config.getConfigurationSection("powertools").getValues(false);
|
||||
if (powertools == null) {
|
||||
continue;
|
||||
}
|
||||
for (final Map.Entry<String, Object> entry : powertools.entrySet()) {
|
||||
if (entry.getValue() instanceof String) {
|
||||
final List<String> temp = new ArrayList<>();
|
||||
temp.add((String) entry.getValue());
|
||||
powertools.put(entry.getKey(), temp);
|
||||
}
|
||||
}
|
||||
config.forceSave();
|
||||
}
|
||||
} catch (final RuntimeException ex) {
|
||||
LOGGER.log(Level.INFO, "File: " + file.toString());
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
doneFile.setProperty("updateUsersPowerToolsFormat", true);
|
||||
doneFile.save();
|
||||
}
|
||||
|
||||
private void updateUsersHomesFormat() {
|
||||
if (doneFile.getBoolean("updateUsersHomesFormat", false)) {
|
||||
return;
|
||||
}
|
||||
final File userdataFolder = new File(ess.getDataFolder(), "userdata");
|
||||
if (!userdataFolder.exists() || !userdataFolder.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
final File[] userFiles = userdataFolder.listFiles();
|
||||
|
||||
for (final File file : userFiles) {
|
||||
if (!file.isFile() || !file.getName().endsWith(".yml")) {
|
||||
continue;
|
||||
}
|
||||
final EssentialsConf config = new EssentialsConf(file);
|
||||
try {
|
||||
|
||||
config.load();
|
||||
if (config.hasProperty("home") && config.hasProperty("home.default")) {
|
||||
final String defworld = (String) config.getProperty("home.default");
|
||||
final Location defloc = getFakeLocation(config, "home.worlds." + defworld);
|
||||
if (defloc != null) {
|
||||
config.setProperty("homes.home", defloc);
|
||||
}
|
||||
|
||||
final Set<String> worlds = config.getConfigurationSection("home.worlds").getKeys(false);
|
||||
Location loc;
|
||||
String worldName;
|
||||
|
||||
if (worlds == null) {
|
||||
continue;
|
||||
}
|
||||
for (final String world : worlds) {
|
||||
if (defworld.equalsIgnoreCase(world)) {
|
||||
continue;
|
||||
}
|
||||
loc = getFakeLocation(config, "home.worlds." + world);
|
||||
if (loc == null) {
|
||||
continue;
|
||||
}
|
||||
worldName = loc.getWorld().getName().toLowerCase(Locale.ENGLISH);
|
||||
if (worldName != null && !worldName.isEmpty()) {
|
||||
config.setProperty("homes." + worldName, loc);
|
||||
}
|
||||
}
|
||||
config.removeProperty("home");
|
||||
config.forceSave();
|
||||
}
|
||||
|
||||
} catch (final RuntimeException ex) {
|
||||
LOGGER.log(Level.INFO, "File: " + file.toString());
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
doneFile.setProperty("updateUsersHomesFormat", true);
|
||||
doneFile.save();
|
||||
}
|
||||
|
||||
private void sanitizeAllUserFilenames() {
|
||||
if (doneFile.getBoolean("sanitizeAllUserFilenames", false)) {
|
||||
return;
|
||||
}
|
||||
final File usersFolder = new File(ess.getDataFolder(), "userdata");
|
||||
if (!usersFolder.exists()) {
|
||||
return;
|
||||
}
|
||||
final File[] listOfFiles = usersFolder.listFiles();
|
||||
for (final File listOfFile : listOfFiles) {
|
||||
final String filename = listOfFile.getName();
|
||||
if (!listOfFile.isFile() || !filename.endsWith(".yml")) {
|
||||
continue;
|
||||
}
|
||||
final String sanitizedFilename = StringUtil.sanitizeFileName(filename.substring(0, filename.length() - 4)) + ".yml";
|
||||
if (sanitizedFilename.equals(filename)) {
|
||||
continue;
|
||||
}
|
||||
final File tmpFile = new File(listOfFile.getParentFile(), sanitizedFilename + ".tmp");
|
||||
final File newFile = new File(listOfFile.getParentFile(), sanitizedFilename);
|
||||
if (!listOfFile.renameTo(tmpFile)) {
|
||||
LOGGER.log(Level.WARNING, tl("userdataMoveError", filename, sanitizedFilename));
|
||||
continue;
|
||||
}
|
||||
if (newFile.exists()) {
|
||||
LOGGER.log(Level.WARNING, tl("duplicatedUserdata", filename, sanitizedFilename));
|
||||
continue;
|
||||
}
|
||||
if (!tmpFile.renameTo(newFile)) {
|
||||
LOGGER.log(Level.WARNING, tl("userdataMoveBackError", sanitizedFilename, sanitizedFilename));
|
||||
}
|
||||
}
|
||||
doneFile.setProperty("sanitizeAllUserFilenames", true);
|
||||
doneFile.save();
|
||||
}
|
||||
|
||||
private World getFakeWorld(final String name) {
|
||||
final File bukkitDirectory = ess.getDataFolder().getParentFile().getParentFile();
|
||||
final File worldDirectory = new File(bukkitDirectory, name);
|
||||
if (worldDirectory.exists() && worldDirectory.isDirectory()) {
|
||||
return new FakeWorld(worldDirectory.getName(), World.Environment.NORMAL);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Location getFakeLocation(final EssentialsConf config, final String path) {
|
||||
final String worldName = config.getString((path != null ? path + "." : "") + "world");
|
||||
if (worldName == null || worldName.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final World world = getFakeWorld(worldName);
|
||||
if (world == null) {
|
||||
return null;
|
||||
}
|
||||
return new Location(world, config.getDouble((path != null ? path + "." : "") + "x", 0), config.getDouble((path != null ? path + "." : "") + "y", 0), config.getDouble((path != null ? path + "." : "") + "z", 0), (float) config.getDouble((path != null ? path + "." : "") + "yaw", 0), (float) config.getDouble((path != null ? path + "." : "") + "pitch", 0));
|
||||
}
|
||||
|
||||
private void deleteOldItemsCsv() {
|
||||
if (doneFile.getBoolean("deleteOldItemsCsv", false)) {
|
||||
return;
|
||||
}
|
||||
final File file = new File(ess.getDataFolder(), "items.csv");
|
||||
if (file.exists()) {
|
||||
try {
|
||||
final Set<BigInteger> oldconfigs = new HashSet<>();
|
||||
oldconfigs.add(new BigInteger("66ec40b09ac167079f558d1099e39f10", 16)); // sep 1
|
||||
oldconfigs.add(new BigInteger("34284de1ead43b0bee2aae85e75c041d", 16)); // crlf
|
||||
oldconfigs.add(new BigInteger("c33bc9b8ee003861611bbc2f48eb6f4f", 16)); // jul 24
|
||||
oldconfigs.add(new BigInteger("6ff17925430735129fc2a02f830c1daa", 16)); // crlf
|
||||
|
||||
final MessageDigest digest = ManagedFile.getDigest();
|
||||
final BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
|
||||
final byte[] buffer = new byte[1024];
|
||||
try (final DigestInputStream dis = new DigestInputStream(bis, digest)) {
|
||||
while (dis.read(buffer) != -1) {
|
||||
}
|
||||
}
|
||||
|
||||
final BigInteger hash = new BigInteger(1, digest.digest());
|
||||
if (oldconfigs.contains(hash) && !file.delete()) {
|
||||
throw new IOException("Could not delete file " + file.toString());
|
||||
}
|
||||
doneFile.setProperty("deleteOldItemsCsv", true);
|
||||
doneFile.save();
|
||||
} catch (final IOException ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSpawnsToNewSpawnsConfig() {
|
||||
if (doneFile.getBoolean("updateSpawnsToNewSpawnsConfig", false)) {
|
||||
return;
|
||||
}
|
||||
final File configFile = new File(ess.getDataFolder(), "spawn.yml");
|
||||
if (configFile.exists()) {
|
||||
|
||||
final EssentialsConf config = new EssentialsConf(configFile);
|
||||
try {
|
||||
config.load();
|
||||
if (!config.hasProperty("spawns")) {
|
||||
final Spawns spawns = new Spawns();
|
||||
final Set<String> keys = config.getKeys(false);
|
||||
for (final String group : keys) {
|
||||
final Location loc = getFakeLocation(config, group);
|
||||
spawns.getSpawns().put(group.toLowerCase(Locale.ENGLISH), loc);
|
||||
}
|
||||
if (!configFile.renameTo(new File(ess.getDataFolder(), "spawn.yml.old"))) {
|
||||
throw new Exception(tl("fileRenameError", "spawn.yml"));
|
||||
}
|
||||
try (final PrintWriter writer = new PrintWriter(configFile)) {
|
||||
new YamlStorageWriter(writer).save(spawns);
|
||||
}
|
||||
}
|
||||
} catch (final Exception ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
doneFile.setProperty("updateSpawnsToNewSpawnsConfig", true);
|
||||
doneFile.save();
|
||||
}
|
||||
|
||||
private void updateJailsToNewJailsConfig() {
|
||||
if (doneFile.getBoolean("updateJailsToNewJailsConfig", false)) {
|
||||
return;
|
||||
}
|
||||
final File configFile = new File(ess.getDataFolder(), "jail.yml");
|
||||
if (configFile.exists()) {
|
||||
|
||||
final EssentialsConf config = new EssentialsConf(configFile);
|
||||
try {
|
||||
config.load();
|
||||
if (!config.hasProperty("jails")) {
|
||||
final com.earth2me.essentials.settings.Jails jails = new com.earth2me.essentials.settings.Jails();
|
||||
final Set<String> keys = config.getKeys(false);
|
||||
for (final String jailName : keys) {
|
||||
final Location loc = getFakeLocation(config, jailName);
|
||||
jails.getJails().put(jailName.toLowerCase(Locale.ENGLISH), loc);
|
||||
}
|
||||
if (!configFile.renameTo(new File(ess.getDataFolder(), "jail.yml.old"))) {
|
||||
throw new Exception(tl("fileRenameError", "jail.yml"));
|
||||
}
|
||||
try (final PrintWriter writer = new PrintWriter(configFile)) {
|
||||
new YamlStorageWriter(writer).save(jails);
|
||||
}
|
||||
}
|
||||
} catch (final Exception ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
doneFile.setProperty("updateJailsToNewJailsConfig", true);
|
||||
doneFile.save();
|
||||
}
|
||||
|
||||
private void warnMetrics() {
|
||||
if (doneFile.getBoolean("warnMetrics", false)) {
|
||||
return;
|
||||
}
|
||||
doneFile.setProperty("warnMetrics", true);
|
||||
doneFile.save();
|
||||
}
|
||||
|
||||
private void uuidFileChange() {
|
||||
if (doneFile.getBoolean("uuidFileChange", false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Boolean ignoreUFCache = doneFile.getBoolean("ignore-userfiles-cache", false);
|
||||
|
||||
final File userdir = new File(ess.getDataFolder(), "userdata");
|
||||
if (!userdir.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int countFiles = 0;
|
||||
int countReqFiles = 0;
|
||||
for (final String string : userdir.list()) {
|
||||
if (!string.endsWith(".yml") || string.length() < 5) {
|
||||
continue;
|
||||
}
|
||||
|
||||
countFiles++;
|
||||
|
||||
final String name = string.substring(0, string.length() - 4);
|
||||
UUID uuid = null;
|
||||
|
||||
try {
|
||||
uuid = UUID.fromString(name);
|
||||
} catch (final IllegalArgumentException ex) {
|
||||
countReqFiles++;
|
||||
}
|
||||
|
||||
if (countFiles > 100) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (countReqFiles < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
ess.getLogger().info("#### Starting Essentials UUID userdata conversion in a few seconds. ####");
|
||||
ess.getLogger().info("We recommend you take a backup of your server before upgrading from the old username system.");
|
||||
|
||||
try {
|
||||
Thread.sleep(15000);
|
||||
} catch (final InterruptedException ex) {
|
||||
// NOOP
|
||||
}
|
||||
|
||||
uuidFileConvert(ess, ignoreUFCache);
|
||||
|
||||
doneFile.setProperty("uuidFileChange", true);
|
||||
doneFile.save();
|
||||
}
|
||||
|
||||
public void banFormatChange() {
|
||||
if (doneFile.getBoolean("banFormatChange", false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ess.getLogger().info("Starting Essentials ban format conversion");
|
||||
|
||||
final File userdir = new File(ess.getDataFolder(), "userdata");
|
||||
if (!userdir.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int countFiles = 0;
|
||||
|
||||
ess.getLogger().info("Found " + userdir.list().length + " files to convert...");
|
||||
|
||||
for (final String string : userdir.list()) {
|
||||
if (!string.endsWith(".yml") || string.length() < 5) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final int showProgress = countFiles % 250;
|
||||
|
||||
if (showProgress == 0) {
|
||||
ess.getLogger().info("Converted " + countFiles + "/" + userdir.list().length);
|
||||
}
|
||||
|
||||
countFiles++;
|
||||
final File pFile = new File(userdir, string);
|
||||
final EssentialsConf conf = new EssentialsConf(pFile);
|
||||
conf.load();
|
||||
|
||||
String banReason;
|
||||
long banTimeout;
|
||||
|
||||
try {
|
||||
banReason = conf.getConfigurationSection("ban").getString("reason");
|
||||
} catch (final NullPointerException n) {
|
||||
banReason = null;
|
||||
}
|
||||
|
||||
final String playerName = conf.getString("lastAccountName");
|
||||
if (playerName != null && playerName.length() > 1 && banReason != null && banReason.length() > 1) {
|
||||
try {
|
||||
if (conf.getConfigurationSection("ban").contains("timeout")) {
|
||||
banTimeout = Long.parseLong(conf.getConfigurationSection("ban").getString("timeout"));
|
||||
} else {
|
||||
banTimeout = 0L;
|
||||
}
|
||||
} catch (final NumberFormatException n) {
|
||||
banTimeout = 0L;
|
||||
}
|
||||
|
||||
if (BanLookup.isBanned(ess, playerName)) {
|
||||
updateBan(playerName, banReason, banTimeout);
|
||||
}
|
||||
}
|
||||
conf.removeProperty("ban");
|
||||
conf.save();
|
||||
}
|
||||
|
||||
doneFile.setProperty("banFormatChange", true);
|
||||
doneFile.save();
|
||||
ess.getLogger().info("Ban format update complete.");
|
||||
}
|
||||
|
||||
private void updateBan(final String playerName, final String banReason, final Long banTimeout) {
|
||||
if (banTimeout == 0) {
|
||||
Bukkit.getBanList(BanList.Type.NAME).addBan(playerName, banReason, null, Console.NAME);
|
||||
} else {
|
||||
Bukkit.getBanList(BanList.Type.NAME).addBan(playerName, banReason, new Date(banTimeout), Console.NAME);
|
||||
}
|
||||
}
|
||||
|
||||
private void repairUserMap() {
|
||||
if (doneFile.getBoolean("userMapRepaired", false)) {
|
||||
return;
|
||||
}
|
||||
ess.getLogger().info("Starting usermap repair");
|
||||
|
||||
final File userdataFolder = new File(ess.getDataFolder(), "userdata");
|
||||
if (!userdataFolder.isDirectory()) {
|
||||
ess.getLogger().warning("Missing userdata folder, aborting");
|
||||
return;
|
||||
}
|
||||
final File[] files = userdataFolder.listFiles(YML_FILTER);
|
||||
|
||||
final DecimalFormat format = new DecimalFormat("#0.00");
|
||||
final Map<String, UUID> names = Maps.newHashMap();
|
||||
|
||||
for (int index = 0; index < files.length; index++) {
|
||||
final File file = files[index];
|
||||
try {
|
||||
UUID uuid = null;
|
||||
final String filename = file.getName();
|
||||
final String configData = new String(java.nio.file.Files.readAllBytes(file.toPath()), Charsets.UTF_8);
|
||||
|
||||
if (filename.length() > 36) {
|
||||
try {
|
||||
// ".yml" ending has 4 chars...
|
||||
uuid = UUID.fromString(filename.substring(0, filename.length() - 4));
|
||||
} catch (final IllegalArgumentException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
final Matcher uuidMatcher = PATTERN_CONFIG_UUID.matcher(configData);
|
||||
if (uuidMatcher.find()) {
|
||||
try {
|
||||
uuid = UUID.fromString(uuidMatcher.group(1));
|
||||
} catch (final IllegalArgumentException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (uuid == null) {
|
||||
// Don't import
|
||||
continue;
|
||||
}
|
||||
|
||||
final Matcher nameMatcher = PATTERN_CONFIG_NAME.matcher(configData);
|
||||
if (nameMatcher.find()) {
|
||||
final String username = nameMatcher.group(1);
|
||||
if (username != null && username.length() > 0) {
|
||||
names.put(StringUtil.safeString(username), uuid);
|
||||
}
|
||||
}
|
||||
|
||||
if (index % 1000 == 0) {
|
||||
ess.getLogger().info("Reading: " + format.format((100d * (double) index) / files.length)
|
||||
+ "%");
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
ess.getLogger().log(Level.SEVERE, "Error while reading file: ", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ess.getUserMap().getNames().putAll(names);
|
||||
ess.getUserMap().reloadConfig();
|
||||
|
||||
doneFile.setProperty("userMapRepaired", true);
|
||||
doneFile.save();
|
||||
ess.getLogger().info("Completed usermap repair.");
|
||||
}
|
||||
|
||||
public void beforeSettings() {
|
||||
if (!ess.getDataFolder().exists()) {
|
||||
ess.getDataFolder().mkdirs();
|
||||
}
|
||||
moveMotdRulesToFile("motd");
|
||||
moveMotdRulesToFile("rules");
|
||||
}
|
||||
|
||||
public void afterSettings() {
|
||||
sanitizeAllUserFilenames();
|
||||
updateUsersPowerToolsFormat();
|
||||
updateUsersHomesFormat();
|
||||
deleteOldItemsCsv();
|
||||
updateSpawnsToNewSpawnsConfig();
|
||||
updateJailsToNewJailsConfig();
|
||||
uuidFileChange();
|
||||
banFormatChange();
|
||||
warnMetrics();
|
||||
repairUserMap();
|
||||
convertIgnoreList();
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.io.Files;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class EssentialsUserConf extends EssentialsConf {
|
||||
public final String username;
|
||||
public final UUID uuid;
|
||||
|
||||
public EssentialsUserConf(final String username, final UUID uuid, final File configFile) {
|
||||
super(configFile);
|
||||
this.username = username;
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean legacyFileExists() {
|
||||
final File file = new File(configFile.getParentFile(), username + ".yml");
|
||||
return file.exists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertLegacyFile() {
|
||||
final File file = new File(configFile.getParentFile(), username + ".yml");
|
||||
try {
|
||||
Files.move(file, new File(configFile.getParentFile(), uuid + ".yml"));
|
||||
} catch (final IOException ex) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Failed to migrate user: " + username, ex);
|
||||
}
|
||||
|
||||
setProperty("lastAccountName", username);
|
||||
}
|
||||
|
||||
private File getAltFile() {
|
||||
final UUID fn = UUID.nameUUIDFromBytes(("OfflinePlayer:" + username.toLowerCase(Locale.ENGLISH)).getBytes(Charsets.UTF_8));
|
||||
return new File(configFile.getParentFile(), fn.toString() + ".yml");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean altFileExists() {
|
||||
if (username.equals(username.toLowerCase())) {
|
||||
return false;
|
||||
}
|
||||
return getAltFile().exists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convertAltFile() {
|
||||
try {
|
||||
Files.move(getAltFile(), new File(configFile.getParentFile(), uuid + ".yml"));
|
||||
} catch (final IOException ex) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Failed to migrate user: " + username, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import net.ess3.api.IEssentials;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.PropertyResourceBundle;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class I18n implements net.ess3.api.II18n {
|
||||
private static final String MESSAGES = "messages";
|
||||
private static final Pattern NODOUBLEMARK = Pattern.compile("''");
|
||||
private static final ResourceBundle NULL_BUNDLE = new ResourceBundle() {
|
||||
public Enumeration<String> getKeys() {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Object handleGetObject(final String key) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
private static I18n instance;
|
||||
private final transient Locale defaultLocale = Locale.getDefault();
|
||||
private final transient ResourceBundle defaultBundle;
|
||||
private final transient IEssentials ess;
|
||||
private transient Locale currentLocale = defaultLocale;
|
||||
private transient ResourceBundle customBundle;
|
||||
private transient ResourceBundle localeBundle;
|
||||
private transient Map<String, MessageFormat> messageFormatCache = new HashMap<>();
|
||||
|
||||
public I18n(final IEssentials ess) {
|
||||
this.ess = ess;
|
||||
defaultBundle = ResourceBundle.getBundle(MESSAGES, Locale.ENGLISH, new UTF8PropertiesControl());
|
||||
localeBundle = defaultBundle;
|
||||
customBundle = NULL_BUNDLE;
|
||||
}
|
||||
|
||||
public static String tl(final String string, final Object... objects) {
|
||||
if (instance == null) {
|
||||
return "";
|
||||
}
|
||||
if (objects.length == 0) {
|
||||
return NODOUBLEMARK.matcher(instance.translate(string)).replaceAll("'");
|
||||
} else {
|
||||
return instance.format(string, objects);
|
||||
}
|
||||
}
|
||||
|
||||
public static String capitalCase(final String input) {
|
||||
return input == null || input.length() == 0 ? input : input.toUpperCase(Locale.ENGLISH).charAt(0) + input.toLowerCase(Locale.ENGLISH).substring(1);
|
||||
}
|
||||
|
||||
public void onEnable() {
|
||||
instance = this;
|
||||
}
|
||||
|
||||
public void onDisable() {
|
||||
instance = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale getCurrentLocale() {
|
||||
return currentLocale;
|
||||
}
|
||||
|
||||
private String translate(final String string) {
|
||||
try {
|
||||
try {
|
||||
return customBundle.getString(string);
|
||||
} catch (final MissingResourceException ex) {
|
||||
return localeBundle.getString(string);
|
||||
}
|
||||
} catch (final MissingResourceException ex) {
|
||||
Logger.getLogger("Essentials").log(Level.WARNING, String.format("Missing translation key \"%s\" in translation file %s", ex.getKey(), localeBundle.getLocale().toString()), ex);
|
||||
return defaultBundle.getString(string);
|
||||
}
|
||||
}
|
||||
|
||||
public String format(final String string, final Object... objects) {
|
||||
String format = translate(string);
|
||||
MessageFormat messageFormat = messageFormatCache.get(format);
|
||||
if (messageFormat == null) {
|
||||
try {
|
||||
messageFormat = new MessageFormat(format);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
ess.getLogger().log(Level.SEVERE, "Invalid Translation key for '" + string + "': " + e.getMessage());
|
||||
format = format.replaceAll("\\{(\\D*?)\\}", "\\[$1\\]");
|
||||
messageFormat = new MessageFormat(format);
|
||||
}
|
||||
messageFormatCache.put(format, messageFormat);
|
||||
}
|
||||
return messageFormat.format(objects).replace(' ', ' '); // replace nbsp with a space
|
||||
}
|
||||
|
||||
public void updateLocale(final String loc) {
|
||||
if (loc != null && !loc.isEmpty()) {
|
||||
final String[] parts = loc.split("[_\\.]");
|
||||
if (parts.length == 1) {
|
||||
currentLocale = new Locale(parts[0]);
|
||||
}
|
||||
if (parts.length == 2) {
|
||||
currentLocale = new Locale(parts[0], parts[1]);
|
||||
}
|
||||
if (parts.length == 3) {
|
||||
currentLocale = new Locale(parts[0], parts[1], parts[2]);
|
||||
}
|
||||
}
|
||||
ResourceBundle.clearCache();
|
||||
messageFormatCache = new HashMap<>();
|
||||
Logger.getLogger("Essentials").log(Level.INFO, String.format("Using locale %s", currentLocale.toString()));
|
||||
|
||||
try {
|
||||
localeBundle = ResourceBundle.getBundle(MESSAGES, currentLocale, new UTF8PropertiesControl());
|
||||
} catch (final MissingResourceException ex) {
|
||||
localeBundle = NULL_BUNDLE;
|
||||
}
|
||||
|
||||
try {
|
||||
customBundle = ResourceBundle.getBundle(MESSAGES, currentLocale, new FileResClassLoader(I18n.class.getClassLoader(), ess), new UTF8PropertiesControl());
|
||||
} catch (final MissingResourceException ex) {
|
||||
customBundle = NULL_BUNDLE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to load properties files from the plugin directory before falling back to the jar.
|
||||
*/
|
||||
private static class FileResClassLoader extends ClassLoader {
|
||||
private final transient File dataFolder;
|
||||
|
||||
FileResClassLoader(final ClassLoader classLoader, final IEssentials ess) {
|
||||
super(classLoader);
|
||||
this.dataFolder = ess.getDataFolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getResource(final String string) {
|
||||
final File file = new File(dataFolder, string);
|
||||
if (file.exists()) {
|
||||
try {
|
||||
return file.toURI().toURL();
|
||||
} catch (final MalformedURLException ignored) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResourceAsStream(final String string) {
|
||||
final File file = new File(dataFolder, string);
|
||||
if (file.exists()) {
|
||||
try {
|
||||
return new FileInputStream(file);
|
||||
} catch (final FileNotFoundException ignored) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads .properties files as UTF-8 instead of ISO-8859-1, which is the default on Java 8/below.
|
||||
* Java 9 fixes this by defaulting to UTF-8 for .properties files.
|
||||
*/
|
||||
private static class UTF8PropertiesControl extends ResourceBundle.Control {
|
||||
public ResourceBundle newBundle(final String baseName, final Locale locale, final String format, final ClassLoader loader, final boolean reload) throws IOException {
|
||||
final String resourceName = toResourceName(toBundleName(baseName, locale), "properties");
|
||||
ResourceBundle bundle = null;
|
||||
InputStream stream = null;
|
||||
if (reload) {
|
||||
final URL url = loader.getResource(resourceName);
|
||||
if (url != null) {
|
||||
final URLConnection connection = url.openConnection();
|
||||
if (connection != null) {
|
||||
connection.setUseCaches(false);
|
||||
stream = connection.getInputStream();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
stream = loader.getResourceAsStream(resourceName);
|
||||
}
|
||||
if (stream != null) {
|
||||
try {
|
||||
// use UTF-8 here, this is the important bit
|
||||
bundle = new PropertyResourceBundle(new InputStreamReader(stream, StandardCharsets.UTF_8));
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import com.earth2me.essentials.api.IAsyncTeleport;
|
||||
import com.earth2me.essentials.commands.IEssentialsCommand;
|
||||
import net.ess3.api.ITeleport;
|
||||
import net.ess3.api.MaxMoneyException;
|
||||
import net.ess3.api.events.AfkStatusChangeEvent;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Provides access to the user abstraction and stored data. Maintainers should add methods to <i>this interface</i>.
|
||||
*
|
||||
* @deprecated External plugins should use {@link net.ess3.api.IUser} instead of this interface, in case future APIs are added.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface IUser {
|
||||
boolean isAuthorized(String node);
|
||||
|
||||
boolean isAuthorized(IEssentialsCommand cmd);
|
||||
|
||||
boolean isAuthorized(IEssentialsCommand cmd, String permissionPrefix);
|
||||
|
||||
boolean isPermissionSet(String node);
|
||||
|
||||
void healCooldown() throws Exception;
|
||||
|
||||
void giveMoney(BigDecimal value) throws MaxMoneyException;
|
||||
|
||||
void giveMoney(final BigDecimal value, final CommandSource initiator) throws MaxMoneyException;
|
||||
|
||||
void payUser(final User reciever, final BigDecimal value) throws Exception;
|
||||
|
||||
void takeMoney(BigDecimal value);
|
||||
|
||||
void takeMoney(final BigDecimal value, final CommandSource initiator);
|
||||
|
||||
boolean canAfford(BigDecimal value);
|
||||
|
||||
Boolean canSpawnItem(final Material material);
|
||||
|
||||
void setLastLocation();
|
||||
|
||||
void setLogoutLocation();
|
||||
|
||||
void requestTeleport(final User player, final boolean here);
|
||||
|
||||
/**
|
||||
* Returns whether this user has an outstanding teleport request to deal with.
|
||||
*
|
||||
* @return whether there is a teleport request
|
||||
*/
|
||||
boolean hasOutstandingTeleportRequest();
|
||||
|
||||
/**
|
||||
* @deprecated This API is not asynchronous. Use {@link com.earth2me.essentials.api.IAsyncTeleport IAsyncTeleport} with {@link IUser#getAsyncTeleport()}
|
||||
*/
|
||||
@Deprecated
|
||||
ITeleport getTeleport();
|
||||
|
||||
IAsyncTeleport getAsyncTeleport();
|
||||
|
||||
BigDecimal getMoney();
|
||||
|
||||
void setMoney(final BigDecimal value) throws MaxMoneyException;
|
||||
|
||||
void setAfk(final boolean set, final AfkStatusChangeEvent.Cause cause);
|
||||
|
||||
/**
|
||||
* 'Hidden' Represents when a player is hidden from others. This status includes when the player is hidden via other
|
||||
* supported plugins. Use isVanished() if you want to check if a user is vanished by Essentials.
|
||||
*
|
||||
* @return If the user is hidden or not
|
||||
* @see IUser#isVanished()
|
||||
*/
|
||||
boolean isHidden();
|
||||
|
||||
void setHidden(boolean vanish);
|
||||
|
||||
boolean isGodModeEnabled();
|
||||
|
||||
String getGroup();
|
||||
|
||||
boolean inGroup(final String group);
|
||||
|
||||
boolean canBuild();
|
||||
|
||||
long getTeleportRequestTime();
|
||||
|
||||
void enableInvulnerabilityAfterTeleport();
|
||||
|
||||
void resetInvulnerabilityAfterTeleport();
|
||||
|
||||
boolean hasInvulnerabilityAfterTeleport();
|
||||
|
||||
/**
|
||||
* 'Vanished' Represents when a player is hidden from others by Essentials. This status does NOT include when the
|
||||
* player is hidden via other plugins. Use isHidden() if you want to check if a user is vanished by any supported
|
||||
* plugin.
|
||||
*
|
||||
* @return If the user is vanished or not
|
||||
* @see IUser#isHidden()
|
||||
*/
|
||||
boolean isVanished();
|
||||
|
||||
void setVanished(boolean vanish);
|
||||
|
||||
boolean isIgnoreExempt();
|
||||
|
||||
void sendMessage(String message);
|
||||
|
||||
/*
|
||||
* UserData
|
||||
*/
|
||||
Location getHome(String name) throws Exception;
|
||||
|
||||
Location getHome(Location loc) throws Exception;
|
||||
|
||||
List<String> getHomes();
|
||||
|
||||
void setHome(String name, Location loc);
|
||||
|
||||
void delHome(String name) throws Exception;
|
||||
|
||||
boolean hasHome();
|
||||
|
||||
Location getLastLocation();
|
||||
|
||||
Location getLogoutLocation();
|
||||
|
||||
long getLastTeleportTimestamp();
|
||||
|
||||
void setLastTeleportTimestamp(long time);
|
||||
|
||||
String getJail();
|
||||
|
||||
void setJail(String jail);
|
||||
|
||||
List<String> getMails();
|
||||
|
||||
void addMail(String mail);
|
||||
|
||||
boolean isAfk();
|
||||
|
||||
@Deprecated
|
||||
void setAfk(final boolean set);
|
||||
|
||||
boolean isIgnoreMsg();
|
||||
|
||||
void setIgnoreMsg(boolean ignoreMsg);
|
||||
|
||||
void setConfigProperty(String node, Object object);
|
||||
|
||||
Set<String> getConfigKeys();
|
||||
|
||||
Map<String, Object> getConfigMap();
|
||||
|
||||
Map<String, Object> getConfigMap(String node);
|
||||
|
||||
Map<Pattern, Long> getCommandCooldowns();
|
||||
|
||||
Date getCommandCooldownExpiry(String label);
|
||||
|
||||
void addCommandCooldown(Pattern pattern, Date expiresAt, boolean save);
|
||||
|
||||
boolean clearCommandCooldown(Pattern pattern);
|
||||
|
||||
/*
|
||||
* PlayerExtension
|
||||
*/
|
||||
Player getBase();
|
||||
|
||||
CommandSource getSource();
|
||||
|
||||
String getName();
|
||||
|
||||
String getDisplayName();
|
||||
|
||||
String getAfkMessage();
|
||||
|
||||
void setAfkMessage(final String message);
|
||||
|
||||
long getAfkSince();
|
||||
|
||||
boolean isAcceptingPay();
|
||||
|
||||
void setAcceptingPay(boolean acceptingPay);
|
||||
|
||||
boolean isPromptingPayConfirm();
|
||||
|
||||
void setPromptingPayConfirm(boolean prompt);
|
||||
|
||||
boolean isPromptingClearConfirm();
|
||||
|
||||
void setPromptingClearConfirm(boolean prompt);
|
||||
|
||||
boolean isLastMessageReplyRecipient();
|
||||
|
||||
void setLastMessageReplyRecipient(boolean enabled);
|
||||
|
||||
Map<User, BigDecimal> getConfirmingPayments();
|
||||
|
||||
Block getTargetBlock(int maxDistance);
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import com.earth2me.essentials.utils.NumberUtil;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.MemoryConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.earth2me.essentials.I18n.capitalCase;
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Kits implements IConf {
|
||||
|
||||
private final EssentialsConf config;
|
||||
private ConfigurationSection kits;
|
||||
|
||||
public Kits(final IEssentials essentials) {
|
||||
config = new EssentialsConf(new File(essentials.getDataFolder(), "kits.yml"));
|
||||
config.setTemplateName("/kits.yml");
|
||||
|
||||
reloadConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadConfig() {
|
||||
config.load();
|
||||
kits = _getKits();
|
||||
}
|
||||
|
||||
private ConfigurationSection _getKits() {
|
||||
if (config.isConfigurationSection("kits")) {
|
||||
final ConfigurationSection section = config.getConfigurationSection("kits");
|
||||
final ConfigurationSection newSection = new MemoryConfiguration();
|
||||
for (final String kitItem : section.getKeys(false)) {
|
||||
if (section.isConfigurationSection(kitItem)) {
|
||||
newSection.set(kitItem.toLowerCase(Locale.ENGLISH), section.getConfigurationSection(kitItem));
|
||||
}
|
||||
}
|
||||
return newSection;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public EssentialsConf getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public ConfigurationSection getKits() {
|
||||
return kits;
|
||||
}
|
||||
|
||||
public Map<String, Object> getKit(String name) {
|
||||
name = name.replace('.', '_').replace('/', '_');
|
||||
if (getKits() != null) {
|
||||
final ConfigurationSection kits = getKits();
|
||||
// For some reason, YAML doesn't sees keys as always lowercase even if they aren't defined like that.
|
||||
// Workaround is to toLowercase when getting from the config, but showing normally elsewhere.
|
||||
// ODDLY ENOUGH when you get the configuration section for ALL kits, it will return the proper
|
||||
// case of each kit. But when you check for each kit's configuration section, it won't return the kit
|
||||
// you just found if you don't toLowercase it.
|
||||
if (kits.isConfigurationSection(name.toLowerCase())) {
|
||||
return kits.getConfigurationSection(name.toLowerCase()).getValues(true);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Tries to find an existing kit name that matches the given name, ignoring case. Returns null if no match.
|
||||
public String matchKit(final String name) {
|
||||
if (config.isConfigurationSection("kits")) {
|
||||
final ConfigurationSection section = config.getConfigurationSection("kits");
|
||||
if (section != null) {
|
||||
for (final String kitName : section.getKeys(false)) {
|
||||
if (kitName.equalsIgnoreCase(name)) {
|
||||
return kitName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addKit(final String name, final List<String> lines, final long delay) {
|
||||
// Will overwrite but w/e
|
||||
config.set("kits." + name + ".delay", delay);
|
||||
config.set("kits." + name + ".items", lines);
|
||||
kits = _getKits();
|
||||
config.save();
|
||||
}
|
||||
|
||||
public void removeKit(final String name) {
|
||||
config.set("kits." + name, null);
|
||||
kits = _getKits();
|
||||
config.save();
|
||||
}
|
||||
|
||||
public String listKits(final net.ess3.api.IEssentials ess, final User user) throws Exception {
|
||||
try {
|
||||
final ConfigurationSection kits = config.getConfigurationSection("kits");
|
||||
final StringBuilder list = new StringBuilder();
|
||||
for (final String kitItem : kits.getKeys(false)) {
|
||||
if (user == null) {
|
||||
list.append(" ").append(capitalCase(kitItem));
|
||||
} else if (user.isAuthorized("essentials.kits." + kitItem.toLowerCase(Locale.ENGLISH))) {
|
||||
String cost = "";
|
||||
String name = capitalCase(kitItem);
|
||||
final BigDecimal costPrice = new Trade("kit-" + kitItem.toLowerCase(Locale.ENGLISH), ess).getCommandCost(user);
|
||||
if (costPrice.signum() > 0) {
|
||||
cost = tl("kitCost", NumberUtil.displayCurrency(costPrice, ess));
|
||||
}
|
||||
|
||||
final Kit kit = new Kit(kitItem, ess);
|
||||
final double nextUse = kit.getNextUse(user);
|
||||
if (nextUse == -1 && ess.getSettings().isSkippingUsedOneTimeKitsFromKitList()) {
|
||||
continue;
|
||||
} else if (nextUse != 0) {
|
||||
name = tl("kitDelay", name);
|
||||
}
|
||||
|
||||
list.append(" ").append(name).append(cost);
|
||||
}
|
||||
}
|
||||
return list.toString().trim();
|
||||
} catch (final Exception ex) {
|
||||
throw new Exception(tl("kitError"), ex);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import com.earth2me.essentials.utils.EnumUtil;
|
||||
import com.earth2me.essentials.utils.VersionUtil;
|
||||
import net.ess3.nms.refl.ReflUtil;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Fox;
|
||||
import org.bukkit.entity.Llama;
|
||||
import org.bukkit.entity.MushroomCow;
|
||||
import org.bukkit.entity.Ocelot;
|
||||
import org.bukkit.entity.Panda;
|
||||
import org.bukkit.entity.Parrot;
|
||||
import org.bukkit.entity.TropicalFish;
|
||||
import org.bukkit.entity.Villager;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static com.earth2me.essentials.utils.EnumUtil.getEntityType;
|
||||
|
||||
public final class MobCompat {
|
||||
|
||||
// Constants for mob interfaces added in later versions
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static final Class RAIDER = ReflUtil.getClassCached("org.bukkit.entity.Raider");
|
||||
|
||||
// Constants for mobs added in later versions
|
||||
public static final EntityType LLAMA = getEntityType("LLAMA");
|
||||
public static final EntityType PARROT = getEntityType("PARROT");
|
||||
public static final EntityType TROPICAL_FISH = getEntityType("TROPICAL_FISH");
|
||||
public static final EntityType PANDA = getEntityType("PANDA");
|
||||
public static final EntityType TRADER_LLAMA = getEntityType("TRADER_LLAMA");
|
||||
public static final EntityType SHULKER = getEntityType("SHULKER");
|
||||
public static final EntityType STRAY = getEntityType("STRAY");
|
||||
public static final EntityType FOX = getEntityType("FOX");
|
||||
public static final EntityType PHANTOM = getEntityType("PHANTOM");
|
||||
|
||||
// Constants for mobs that have changed since earlier versions
|
||||
public static final EntityType CAT = getEntityType("CAT", "OCELOT");
|
||||
public static final EntityType ZOMBIFIED_PIGLIN = getEntityType("ZOMBIFIED_PIGLIN", "PIG_ZOMBIE");
|
||||
|
||||
private MobCompat() {
|
||||
}
|
||||
|
||||
// Older cats are Ocelots, whereas 1.14+ cats are Cats
|
||||
public static void setCatType(final Entity entity, final CatType type) {
|
||||
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_14_R01)) {
|
||||
((Ocelot) entity).setCatType(Ocelot.Type.valueOf(type.ocelotTypeName));
|
||||
} else {
|
||||
final Class cat = ReflUtil.getClassCached("org.bukkit.entity.Cat");
|
||||
final Class catType = ReflUtil.getClassCached("org.bukkit.entity.Cat$Type");
|
||||
final Method setCatType = ReflUtil.getMethodCached(cat, "setCatType", catType);
|
||||
try {
|
||||
setCatType.invoke(entity, EnumUtil.valueOf(catType, type.catTypeName));
|
||||
} catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Older villagers have professions and careers, 1.14+ villagers only have professions
|
||||
public static void setVillagerProfession(final Entity entity, final VillagerProfession profession) {
|
||||
if (!(entity instanceof Villager)) {
|
||||
return;
|
||||
}
|
||||
final Villager villager = (Villager) entity;
|
||||
villager.setProfession(profession.asEnum());
|
||||
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_14_R01)) {
|
||||
final Class villagerCareer = ReflUtil.getClassCached("org.bukkit.entity.Villager$Career");
|
||||
final Method setCareer = ReflUtil.getMethodCached(Villager.class, "setCareer", villagerCareer);
|
||||
try {
|
||||
setCareer.invoke(entity, EnumUtil.valueOf(villagerCareer, profession.oldCareer));
|
||||
} catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only 1.14+ villagers have biome variants
|
||||
public static void setVillagerType(final Entity entity, final String type) {
|
||||
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_14_R01)) {
|
||||
return;
|
||||
}
|
||||
if (entity instanceof Villager) {
|
||||
((Villager) entity).setVillagerType(Villager.Type.valueOf(type));
|
||||
}
|
||||
}
|
||||
|
||||
// Llamas only exist in 1.11+
|
||||
public static void setLlamaColor(final Entity entity, final String color) {
|
||||
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_11_R01)) {
|
||||
return;
|
||||
}
|
||||
if (entity instanceof Llama) {
|
||||
((Llama) entity).setColor(Llama.Color.valueOf(color));
|
||||
}
|
||||
}
|
||||
|
||||
// Parrots only exist in 1.12+
|
||||
public static void setParrotVariant(final Entity entity, final String variant) {
|
||||
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_12_0_R01)) {
|
||||
return;
|
||||
}
|
||||
if (entity instanceof Parrot) {
|
||||
((Parrot) entity).setVariant(Parrot.Variant.valueOf(variant));
|
||||
}
|
||||
}
|
||||
|
||||
// Tropical fish only exist in 1.13+
|
||||
public static void setTropicalFishPattern(final Entity entity, final String pattern) {
|
||||
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_12_0_R01)) {
|
||||
return;
|
||||
}
|
||||
if (entity instanceof TropicalFish) {
|
||||
((TropicalFish) entity).setPattern(TropicalFish.Pattern.valueOf(pattern));
|
||||
}
|
||||
}
|
||||
|
||||
// Mushroom cow variant API only exists in 1.14+
|
||||
public static void setMooshroomVariant(final Entity entity, final String variant) {
|
||||
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_14_R01)) {
|
||||
return;
|
||||
}
|
||||
if (entity instanceof MushroomCow) {
|
||||
((MushroomCow) entity).setVariant(MushroomCow.Variant.valueOf(variant));
|
||||
}
|
||||
}
|
||||
|
||||
// Pandas only exists in 1.14+
|
||||
public static void setPandaGene(final Entity entity, final String gene, final boolean mainGene) {
|
||||
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_14_R01)) {
|
||||
return;
|
||||
}
|
||||
if (entity instanceof Panda) {
|
||||
final Panda panda = (Panda) entity;
|
||||
final Panda.Gene pandaGene = Panda.Gene.valueOf(gene);
|
||||
if (mainGene) {
|
||||
panda.setMainGene(pandaGene);
|
||||
} else {
|
||||
panda.setHiddenGene(pandaGene);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Foxes only exist in 1.14+
|
||||
public static void setFoxType(final Entity entity, final String type) {
|
||||
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_14_R01)) {
|
||||
return;
|
||||
}
|
||||
if (entity instanceof Fox) {
|
||||
((Fox) entity).setFoxType(Fox.Type.valueOf(type));
|
||||
}
|
||||
}
|
||||
|
||||
public enum CatType {
|
||||
// These are (loosely) Mojang names for the cats
|
||||
SIAMESE("SIAMESE", "SIAMESE_CAT"),
|
||||
WHITE("WHITE", "SIAMESE_CAT"),
|
||||
RED("RED", "RED_CAT"),
|
||||
TABBY("TABBY", "RED_CAT"),
|
||||
TUXEDO("BLACK", "BLACK_CAT"),
|
||||
BRITISH_SHORTHAIR("BRITISH_SHORTHAIR", "SIAMESE_CAT"),
|
||||
CALICO("CALICO", "RED_CAT"),
|
||||
PERSIAN("PERSIAN", "RED_CAT"),
|
||||
RAGDOLL("RAGDOLL", "SIAMESE_CAT"),
|
||||
JELLIE("JELLIE", "SIAMESE_CAT"),
|
||||
BLACK("ALL_BLACK", "BLACK_CAT"),
|
||||
;
|
||||
|
||||
private final String catTypeName;
|
||||
private final String ocelotTypeName;
|
||||
|
||||
CatType(final String catTypeName, final String ocelotTypeName) {
|
||||
this.catTypeName = catTypeName;
|
||||
this.ocelotTypeName = ocelotTypeName;
|
||||
}
|
||||
}
|
||||
|
||||
public enum VillagerProfession {
|
||||
// These are 1.14+ villager professions mapped to their respective pre-V&P profession and career
|
||||
NONE("FARMER", "FARMER", "NONE"),
|
||||
ARMORER("BLACKSMITH", "ARMORER"),
|
||||
BUTCHER("FARMER", "BUTCHER"),
|
||||
CARTOGRAPHER("LIBRARIAN", "CARTOGRAPHER"),
|
||||
CLERIC("PRIEST", "CLERIC"),
|
||||
FARMER("FARMER", "FARMER"),
|
||||
FISHERMAN("FARMER", "FISHERMAN"),
|
||||
FLETCHER("FARMER", "FLETCHER"),
|
||||
LEATHERWORKER("BUTCHER", "LEATHERWORKER"),
|
||||
LIBRARIAN("LIBRARIAN", "LIBRARIAN"),
|
||||
MASON(null, null, "MASON"),
|
||||
NITWIT("NITWIT", "NITWIT"),
|
||||
SHEPHERD("FARMER", "SHEPHERD"),
|
||||
TOOLSMITH("BLACKSMITH", "TOOL_SMITH", "TOOLSMITH"),
|
||||
WEAPONSMITH("BLACKSMITH", "WEAPON_SMITH", "WEAPONSMITH"),
|
||||
;
|
||||
|
||||
private final String oldProfession;
|
||||
private final String oldCareer;
|
||||
private final String newProfession;
|
||||
|
||||
VillagerProfession(final String oldProfession, final String career) {
|
||||
this.oldProfession = oldProfession;
|
||||
this.oldCareer = career;
|
||||
this.newProfession = career;
|
||||
}
|
||||
|
||||
VillagerProfession(final String oldProfession, final String oldCareer, final String newProfession) {
|
||||
this.oldProfession = oldProfession;
|
||||
this.oldCareer = oldCareer;
|
||||
this.newProfession = newProfession;
|
||||
}
|
||||
|
||||
private Villager.Profession asEnum() {
|
||||
return EnumUtil.valueOf(Villager.Profession.class, newProfession, oldProfession);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import com.earth2me.essentials.utils.FormatUtil;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Server;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public final class PlayerList {
|
||||
|
||||
private PlayerList() {
|
||||
}
|
||||
|
||||
// Cosmetic list formatting
|
||||
public static String listUsers(final IEssentials ess, final List<User> users, final String seperator) {
|
||||
final StringBuilder groupString = new StringBuilder();
|
||||
Collections.sort(users);
|
||||
boolean needComma = false;
|
||||
for (final User user : users) {
|
||||
if (needComma) {
|
||||
groupString.append(seperator);
|
||||
}
|
||||
needComma = true;
|
||||
if (user.isAfk()) {
|
||||
groupString.append(tl("listAfkTag"));
|
||||
}
|
||||
if (user.isHidden()) {
|
||||
groupString.append(tl("listHiddenTag"));
|
||||
}
|
||||
user.setDisplayNick();
|
||||
groupString.append(user.getDisplayName());
|
||||
if (ess.getSettings().realNamesOnList() && !ChatColor.stripColor(user.getDisplayName()).equals(user.getName())) {
|
||||
groupString.append(" (").append(user.getName()).append(")");
|
||||
}
|
||||
groupString.append("\u00a7f");
|
||||
}
|
||||
return groupString.toString();
|
||||
}
|
||||
|
||||
// Produce a user summary: There are 5 out of maximum 10 players online.
|
||||
public static String listSummary(final IEssentials ess, final User user, final boolean showHidden) {
|
||||
final Server server = ess.getServer();
|
||||
int playerHidden = 0;
|
||||
int hiddenCount = 0;
|
||||
for (final User onlinePlayer : ess.getOnlineUsers()) {
|
||||
if (onlinePlayer.isHidden() || (user != null && !user.getBase().canSee(onlinePlayer.getBase()))) {
|
||||
playerHidden++;
|
||||
if (showHidden || user != null && user.getBase().canSee(onlinePlayer.getBase())) {
|
||||
hiddenCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
final String online;
|
||||
if (hiddenCount > 0) {
|
||||
online = tl("listAmountHidden", ess.getOnlinePlayers().size() - playerHidden, hiddenCount, server.getMaxPlayers());
|
||||
} else {
|
||||
online = tl("listAmount", ess.getOnlinePlayers().size() - playerHidden, server.getMaxPlayers());
|
||||
}
|
||||
return online;
|
||||
}
|
||||
|
||||
// Build the basic player list, divided by groups.
|
||||
public static Map<String, List<User>> getPlayerLists(final IEssentials ess, final IUser sender, final boolean showHidden) {
|
||||
final Map<String, List<User>> playerList = new HashMap<>();
|
||||
for (final User onlineUser : ess.getOnlineUsers()) {
|
||||
if ((sender == null && !showHidden && onlineUser.isHidden()) || (sender != null && !showHidden && !sender.getBase().canSee(onlineUser.getBase()))) {
|
||||
continue;
|
||||
}
|
||||
final String group = FormatUtil.stripFormat(FormatUtil.stripEssentialsFormat(onlineUser.getGroup().toLowerCase()));
|
||||
final List<User> list = playerList.computeIfAbsent(group, k -> new ArrayList<>());
|
||||
list.add(onlineUser);
|
||||
}
|
||||
return playerList;
|
||||
}
|
||||
|
||||
// Handle the merging of groups
|
||||
public static List<User> getMergedList(final IEssentials ess, final Map<String, List<User>> playerList, final String groupName) {
|
||||
final Set<String> configGroups = ess.getSettings().getListGroupConfig().keySet();
|
||||
final List<User> users = new ArrayList<>();
|
||||
for (final String configGroup : configGroups) {
|
||||
if (configGroup.equalsIgnoreCase(groupName)) {
|
||||
final String[] groupValues = ess.getSettings().getListGroupConfig().get(configGroup).toString().trim().split(" ");
|
||||
for (String groupValue : groupValues) {
|
||||
groupValue = groupValue.toLowerCase(Locale.ENGLISH);
|
||||
if (groupValue.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
final List<User> u = playerList.get(groupValue.trim());
|
||||
if (u == null || u.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
playerList.remove(groupValue);
|
||||
users.addAll(u);
|
||||
}
|
||||
}
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
// Output a playerlist of just a single group, /list <groupname>
|
||||
public static String listGroupUsers(final IEssentials ess, final Map<String, List<User>> playerList, final String groupName) throws Exception {
|
||||
final List<User> users = getMergedList(ess, playerList, groupName);
|
||||
final List<User> groupUsers = playerList.get(groupName);
|
||||
if (groupUsers != null && !groupUsers.isEmpty()) {
|
||||
users.addAll(groupUsers);
|
||||
}
|
||||
if (users.isEmpty()) {
|
||||
throw new Exception(tl("groupDoesNotExist"));
|
||||
}
|
||||
final String displayGroupName = Character.toTitleCase(groupName.charAt(0)) +
|
||||
groupName.substring(1);
|
||||
return outputFormat(displayGroupName, listUsers(ess, users, ", "));
|
||||
}
|
||||
|
||||
// Build the output string
|
||||
public static String outputFormat(final String group, final String message) {
|
||||
return tl("listGroupTag", FormatUtil.replaceFormat(group)) +
|
||||
message;
|
||||
}
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import com.earth2me.essentials.utils.LocationUtil;
|
||||
import com.earth2me.essentials.utils.VersionUtil;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import net.ess3.api.InvalidWorldException;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
public class RandomTeleport implements IConf {
|
||||
private static final Random RANDOM = new Random();
|
||||
private static final int HIGHEST_BLOCK_Y_OFFSET = VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_15_R01) ? 1 : 0;
|
||||
private final IEssentials essentials;
|
||||
private final EssentialsConf config;
|
||||
private final ConcurrentLinkedQueue<Location> cachedLocations = new ConcurrentLinkedQueue<>();
|
||||
|
||||
public RandomTeleport(final IEssentials essentials) {
|
||||
this.essentials = essentials;
|
||||
final File file = new File(essentials.getDataFolder(), "tpr.yml");
|
||||
config = new EssentialsConf(file);
|
||||
config.setTemplateName("/tpr.yml");
|
||||
config.options().copyHeader(true);
|
||||
reloadConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadConfig() {
|
||||
config.load();
|
||||
cachedLocations.clear();
|
||||
}
|
||||
|
||||
public Location getCenter() {
|
||||
try {
|
||||
final Location center = config.getLocation("center", essentials.getServer());
|
||||
if (center != null) {
|
||||
return center;
|
||||
}
|
||||
} catch (final InvalidWorldException ignored) {
|
||||
}
|
||||
final Location center = essentials.getServer().getWorlds().get(0).getWorldBorder().getCenter();
|
||||
center.setY(center.getWorld().getHighestBlockYAt(center) + 1);
|
||||
setCenter(center);
|
||||
return center;
|
||||
}
|
||||
|
||||
public void setCenter(final Location center) {
|
||||
config.setProperty("center", center);
|
||||
config.save();
|
||||
}
|
||||
|
||||
public double getMinRange() {
|
||||
return config.getDouble("min-range", 0d);
|
||||
}
|
||||
|
||||
public void setMinRange(final double minRange) {
|
||||
config.setProperty("min-range", minRange);
|
||||
config.save();
|
||||
}
|
||||
|
||||
public double getMaxRange() {
|
||||
return config.getDouble("max-range", getCenter().getWorld().getWorldBorder().getSize() / 2);
|
||||
}
|
||||
|
||||
public void setMaxRange(final double maxRange) {
|
||||
config.setProperty("max-range", maxRange);
|
||||
config.save();
|
||||
}
|
||||
|
||||
public Set<Biome> getExcludedBiomes() {
|
||||
final List<String> biomeNames = config.getStringList("excluded-biomes");
|
||||
final Set<Biome> excludedBiomes = new HashSet<>();
|
||||
for (final String biomeName : biomeNames) {
|
||||
try {
|
||||
excludedBiomes.add(Biome.valueOf(biomeName.toUpperCase()));
|
||||
} catch (final IllegalArgumentException ignored) {
|
||||
}
|
||||
}
|
||||
return excludedBiomes;
|
||||
}
|
||||
|
||||
public int getFindAttempts() {
|
||||
return config.getInt("find-attempts", 10);
|
||||
}
|
||||
|
||||
public int getCacheThreshold() {
|
||||
return config.getInt("cache-threshold", 10);
|
||||
}
|
||||
|
||||
public boolean getPreCache() {
|
||||
return config.getBoolean("pre-cache", false);
|
||||
}
|
||||
|
||||
public Queue<Location> getCachedLocations() {
|
||||
return cachedLocations;
|
||||
}
|
||||
|
||||
// Get a random location; cached if possible. Otherwise on demand.
|
||||
public CompletableFuture<Location> getRandomLocation(final Location center, final double minRange, final double maxRange) {
|
||||
final int findAttempts = this.getFindAttempts();
|
||||
final Queue<Location> cachedLocations = this.getCachedLocations();
|
||||
// Try to build up the cache if it is below the threshold
|
||||
if (cachedLocations.size() < this.getCacheThreshold()) {
|
||||
cacheRandomLocations(center, minRange, maxRange);
|
||||
}
|
||||
final CompletableFuture<Location> future = new CompletableFuture<>();
|
||||
// Return a random location immediately if one is available, otherwise try to find one now
|
||||
if (cachedLocations.isEmpty()) {
|
||||
attemptRandomLocation(findAttempts, center, minRange, maxRange).thenAccept(future::complete);
|
||||
} else {
|
||||
future.complete(cachedLocations.poll());
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
// Prompts caching random valid locations, up to a maximum number of attempts
|
||||
public void cacheRandomLocations(final Location center, final double minRange, final double maxRange) {
|
||||
essentials.getServer().getScheduler().scheduleSyncDelayedTask(essentials, () -> {
|
||||
for (int i = 0; i < this.getFindAttempts(); ++i) {
|
||||
calculateRandomLocation(center, minRange, maxRange).thenAccept(location -> {
|
||||
if (isValidRandomLocation(location)) {
|
||||
this.getCachedLocations().add(location);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Recursively attempt to find a random location. After a maximum number of attempts, the center is returned.
|
||||
private CompletableFuture<Location> attemptRandomLocation(final int attempts, final Location center, final double minRange, final double maxRange) {
|
||||
final CompletableFuture<Location> future = new CompletableFuture<>();
|
||||
if (attempts > 0) {
|
||||
calculateRandomLocation(center, minRange, maxRange).thenAccept(location -> {
|
||||
if (isValidRandomLocation(location)) {
|
||||
future.complete(location);
|
||||
} else {
|
||||
attemptRandomLocation(attempts - 1, center, minRange, maxRange).thenAccept(future::complete);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
future.complete(center);
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
// Calculates a random location asynchronously.
|
||||
private CompletableFuture<Location> calculateRandomLocation(final Location center, final double minRange, final double maxRange) {
|
||||
final CompletableFuture<Location> future = new CompletableFuture<>();
|
||||
// Find an equally distributed offset by randomly rotating a point inside a rectangle about the origin
|
||||
final double rectX = RANDOM.nextDouble() * (maxRange - minRange) + minRange;
|
||||
final double rectZ = RANDOM.nextDouble() * (maxRange + minRange) - minRange;
|
||||
final double offsetX;
|
||||
final double offsetZ;
|
||||
final int transform = RANDOM.nextInt(4);
|
||||
if (transform == 0) {
|
||||
offsetX = rectX;
|
||||
offsetZ = rectZ;
|
||||
} else if (transform == 1) {
|
||||
offsetX = -rectZ;
|
||||
offsetZ = rectX;
|
||||
} else if (transform == 2) {
|
||||
offsetX = -rectX;
|
||||
offsetZ = -rectZ;
|
||||
} else {
|
||||
offsetX = rectZ;
|
||||
offsetZ = -rectX;
|
||||
}
|
||||
final Location location = new Location(
|
||||
center.getWorld(),
|
||||
center.getX() + offsetX,
|
||||
center.getWorld().getMaxHeight(),
|
||||
center.getZ() + offsetZ,
|
||||
360 * RANDOM.nextFloat() - 180,
|
||||
0
|
||||
);
|
||||
PaperLib.getChunkAtAsync(location).thenAccept(chunk -> {
|
||||
if (World.Environment.NETHER.equals(center.getWorld().getEnvironment())) {
|
||||
location.setY(getNetherYAt(location));
|
||||
} else {
|
||||
location.setY(center.getWorld().getHighestBlockYAt(location) + HIGHEST_BLOCK_Y_OFFSET);
|
||||
}
|
||||
future.complete(location);
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
// Returns an appropriate elevation for a given location in the nether, or -1 if none is found
|
||||
private double getNetherYAt(final Location location) {
|
||||
for (int y = 32; y < location.getWorld().getMaxHeight() / 2; ++y) {
|
||||
if (!LocationUtil.isBlockUnsafe(location.getWorld(), location.getBlockX(), y, location.getBlockZ())) {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private boolean isValidRandomLocation(final Location location) {
|
||||
return location.getBlockY() > 0 && !this.getExcludedBiomes().contains(location.getBlock().getBiome());
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import net.ess3.api.IEssentials;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityExplodeEvent;
|
||||
|
||||
public class TNTExplodeListener implements Listener, Runnable {
|
||||
private final transient IEssentials ess;
|
||||
private transient boolean enabled = false;
|
||||
private transient int timer = -1;
|
||||
|
||||
public TNTExplodeListener(final IEssentials ess) {
|
||||
super();
|
||||
this.ess = ess;
|
||||
}
|
||||
|
||||
public void enable() {
|
||||
if (!enabled) {
|
||||
enabled = true;
|
||||
timer = ess.scheduleSyncDelayedTask(this, 200);
|
||||
return;
|
||||
}
|
||||
if (timer != -1) {
|
||||
ess.getScheduler().cancelTask(timer);
|
||||
timer = ess.scheduleSyncDelayedTask(this, 200);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onEntityExplode(final EntityExplodeEvent event) {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
if (event.getEntity() instanceof LivingEntity) {
|
||||
return;
|
||||
}
|
||||
if (event.blockList().size() < 1) {
|
||||
return;
|
||||
}
|
||||
event.setCancelled(true);
|
||||
event.getLocation().getWorld().createExplosion(event.getLocation(), 0F);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
@@ -1,403 +0,0 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import com.earth2me.essentials.utils.DateUtil;
|
||||
import com.earth2me.essentials.utils.LocationUtil;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import net.ess3.api.IEssentials;
|
||||
import net.ess3.api.ITeleport;
|
||||
import net.ess3.api.IUser;
|
||||
import net.ess3.api.events.UserWarpEvent;
|
||||
import net.ess3.api.events.teleport.PreTeleportEvent;
|
||||
import net.ess3.api.events.teleport.TeleportWarmupEvent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
/**
|
||||
* @deprecated This API is not asynchronous. Use {@link com.earth2me.essentials.AsyncTeleport AsyncTeleport}
|
||||
*/
|
||||
@Deprecated
|
||||
public class Teleport implements ITeleport {
|
||||
private final IUser teleportOwner;
|
||||
private final IEssentials ess;
|
||||
private TimedTeleport timedTeleport;
|
||||
|
||||
private TeleportType tpType;
|
||||
|
||||
@Deprecated
|
||||
public Teleport(final IUser user, final IEssentials ess) {
|
||||
this.teleportOwner = user;
|
||||
this.ess = ess;
|
||||
tpType = TeleportType.NORMAL;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void cooldown(final boolean check) throws Exception {
|
||||
final Calendar time = new GregorianCalendar();
|
||||
if (teleportOwner.getLastTeleportTimestamp() > 0) {
|
||||
// Take the current time, and remove the delay from it.
|
||||
final double cooldown = ess.getSettings().getTeleportCooldown();
|
||||
final Calendar earliestTime = new GregorianCalendar();
|
||||
earliestTime.add(Calendar.SECOND, -(int) cooldown);
|
||||
earliestTime.add(Calendar.MILLISECOND, -(int) ((cooldown * 1000.0) % 1000.0));
|
||||
// This value contains the most recent time a teleportPlayer could have been used that would allow another use.
|
||||
final long earliestLong = earliestTime.getTimeInMillis();
|
||||
|
||||
// When was the last teleportPlayer used?
|
||||
final long lastTime = teleportOwner.getLastTeleportTimestamp();
|
||||
|
||||
if (lastTime > time.getTimeInMillis()) {
|
||||
// This is to make sure time didn't get messed up on last teleportPlayer use.
|
||||
// If this happens, let's give the user the benifit of the doubt.
|
||||
teleportOwner.setLastTeleportTimestamp(time.getTimeInMillis());
|
||||
return;
|
||||
} else if (lastTime > earliestLong
|
||||
&& cooldownApplies()) {
|
||||
time.setTimeInMillis(lastTime);
|
||||
time.add(Calendar.SECOND, (int) cooldown);
|
||||
time.add(Calendar.MILLISECOND, (int) ((cooldown * 1000.0) % 1000.0));
|
||||
throw new Exception(tl("timeBeforeTeleport", DateUtil.formatDateDiff(time.getTimeInMillis())));
|
||||
}
|
||||
}
|
||||
// if justCheck is set, don't update lastTeleport; we're just checking
|
||||
if (!check) {
|
||||
teleportOwner.setLastTeleportTimestamp(time.getTimeInMillis());
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private boolean cooldownApplies() {
|
||||
boolean applies = true;
|
||||
final String globalBypassPerm = "essentials.teleport.cooldown.bypass";
|
||||
switch (tpType) {
|
||||
case NORMAL:
|
||||
applies = !teleportOwner.isAuthorized(globalBypassPerm);
|
||||
break;
|
||||
case BACK:
|
||||
applies = !(teleportOwner.isAuthorized(globalBypassPerm) &&
|
||||
teleportOwner.isAuthorized("essentials.teleport.cooldown.bypass.back"));
|
||||
break;
|
||||
case TPA:
|
||||
applies = !(teleportOwner.isAuthorized(globalBypassPerm) &&
|
||||
teleportOwner.isAuthorized("essentials.teleport.cooldown.bypass.tpa"));
|
||||
break;
|
||||
}
|
||||
return applies;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private void warnUser(final IUser user, final double delay) {
|
||||
final Calendar c = new GregorianCalendar();
|
||||
c.add(Calendar.SECOND, (int) delay);
|
||||
c.add(Calendar.MILLISECOND, (int) ((delay * 1000.0) % 1000.0));
|
||||
user.sendMessage(tl("dontMoveMessage", DateUtil.formatDateDiff(c.getTimeInMillis())));
|
||||
}
|
||||
|
||||
//The now function is used when you want to skip tp delay when teleporting someone to a location or player.
|
||||
@Override
|
||||
@Deprecated
|
||||
public void now(final Location loc, final boolean cooldown, final TeleportCause cause) throws Exception {
|
||||
if (cooldown) {
|
||||
cooldown(false);
|
||||
}
|
||||
final ITarget target = new LocationTarget(loc);
|
||||
now(teleportOwner, target, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void now(final Player entity, final boolean cooldown, final TeleportCause cause) throws Exception {
|
||||
if (cooldown) {
|
||||
cooldown(false);
|
||||
}
|
||||
final ITarget target = new PlayerTarget(entity);
|
||||
now(teleportOwner, target, cause);
|
||||
teleportOwner.sendMessage(tl("teleporting", target.getLocation().getWorld().getName(), target.getLocation().getBlockX(), target.getLocation().getBlockY(), target.getLocation().getBlockZ()));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected void now(final IUser teleportee, final ITarget target, final TeleportCause cause) throws Exception {
|
||||
cancel(false);
|
||||
Location loc = target.getLocation();
|
||||
|
||||
final PreTeleportEvent event = new PreTeleportEvent(teleportee, cause, target);
|
||||
Bukkit.getServer().getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (teleportee.isAuthorized("essentials.back.onteleport")) {
|
||||
teleportee.setLastLocation();
|
||||
}
|
||||
|
||||
if (!teleportee.getBase().isEmpty()) {
|
||||
if (!ess.getSettings().isTeleportPassengerDismount()) {
|
||||
throw new Exception(tl("passengerTeleportFail"));
|
||||
}
|
||||
teleportee.getBase().eject();
|
||||
}
|
||||
|
||||
if (LocationUtil.isBlockUnsafeForUser(teleportee, loc.getWorld(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())) {
|
||||
if (ess.getSettings().isTeleportSafetyEnabled()) {
|
||||
if (ess.getSettings().isForceDisableTeleportSafety()) {
|
||||
PaperLib.teleportAsync(teleportee.getBase(), loc, cause);
|
||||
} else {
|
||||
PaperLib.teleportAsync(teleportee.getBase(), LocationUtil.getSafeDestination(ess, teleportee, loc), cause);
|
||||
}
|
||||
} else {
|
||||
throw new Exception(tl("unsafeTeleportDestination", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
|
||||
}
|
||||
} else {
|
||||
if (ess.getSettings().isForceDisableTeleportSafety()) {
|
||||
PaperLib.teleportAsync(teleportee.getBase(), loc, cause);
|
||||
} else {
|
||||
if (ess.getSettings().isTeleportToCenterLocation()) {
|
||||
loc = LocationUtil.getRoundedDestination(loc);
|
||||
}
|
||||
PaperLib.teleportAsync(teleportee.getBase(), loc, cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//The teleportPlayer function is used when you want to normally teleportPlayer someone to a location or player.
|
||||
//This method is nolonger used internally and will be removed.
|
||||
@Deprecated
|
||||
@Override
|
||||
public void teleport(final Location loc, final Trade chargeFor) throws Exception {
|
||||
teleport(loc, chargeFor, TeleportCause.PLUGIN);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void teleport(final Location loc, final Trade chargeFor, final TeleportCause cause) throws Exception {
|
||||
teleport(teleportOwner, new LocationTarget(loc), chargeFor, cause);
|
||||
}
|
||||
|
||||
//This is used when teleporting to a player
|
||||
@Override
|
||||
@Deprecated
|
||||
public void teleport(final Player entity, final Trade chargeFor, final TeleportCause cause) throws Exception {
|
||||
final ITarget target = new PlayerTarget(entity);
|
||||
teleportOwner.sendMessage(tl("teleportToPlayer", entity.getDisplayName()));
|
||||
teleport(teleportOwner, target, chargeFor, cause);
|
||||
}
|
||||
|
||||
//This is used when teleporting to stored location
|
||||
@Override
|
||||
@Deprecated
|
||||
public void teleportPlayer(final IUser teleportee, final Location loc, final Trade chargeFor, final TeleportCause cause) throws Exception {
|
||||
teleport(teleportee, new LocationTarget(loc), chargeFor, cause);
|
||||
}
|
||||
|
||||
//This is used on /tphere
|
||||
@Override
|
||||
@Deprecated
|
||||
public void teleportPlayer(final IUser teleportee, final Player entity, final Trade chargeFor, final TeleportCause cause) throws Exception {
|
||||
final ITarget target = new PlayerTarget(entity);
|
||||
teleport(teleportee, target, chargeFor, cause);
|
||||
teleportee.sendMessage(tl("teleporting", target.getLocation().getWorld().getName(), target.getLocation().getBlockX(), target.getLocation().getBlockY(), target.getLocation().getBlockZ()));
|
||||
teleportOwner.sendMessage(tl("teleporting", target.getLocation().getWorld().getName(), target.getLocation().getBlockX(), target.getLocation().getBlockY(), target.getLocation().getBlockZ()));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private void teleport(final IUser teleportee, final ITarget target, final Trade chargeFor, final TeleportCause cause) throws Exception {
|
||||
double delay = ess.getSettings().getTeleportDelay();
|
||||
|
||||
final TeleportWarmupEvent event = new TeleportWarmupEvent(teleportee, cause, target, delay);
|
||||
Bukkit.getServer().getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
delay = event.getDelay();
|
||||
|
||||
Trade cashCharge = chargeFor;
|
||||
|
||||
if (chargeFor != null) {
|
||||
chargeFor.isAffordableFor(teleportOwner);
|
||||
|
||||
//This code is to make sure that commandcosts are checked in the initial world, and not in the resulting world.
|
||||
if (!chargeFor.getCommandCost(teleportOwner).equals(BigDecimal.ZERO)) {
|
||||
//By converting a command cost to a regular cost, the command cost permission isn't checked when executing the charge after teleport.
|
||||
cashCharge = new Trade(chargeFor.getCommandCost(teleportOwner), ess);
|
||||
}
|
||||
}
|
||||
|
||||
cooldown(true);
|
||||
if (delay <= 0 || teleportOwner.isAuthorized("essentials.teleport.timer.bypass") || teleportee.isAuthorized("essentials.teleport.timer.bypass")) {
|
||||
cooldown(false);
|
||||
now(teleportee, target, cause);
|
||||
if (cashCharge != null) {
|
||||
cashCharge.charge(teleportOwner);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
cancel(false);
|
||||
warnUser(teleportee, delay);
|
||||
initTimer((long) (delay * 1000.0), teleportee, target, cashCharge, cause, false);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private void teleportOther(final IUser teleporter, final IUser teleportee, final ITarget target, final Trade chargeFor, final TeleportCause cause) throws Exception {
|
||||
double delay = ess.getSettings().getTeleportDelay();
|
||||
|
||||
final TeleportWarmupEvent event = new TeleportWarmupEvent(teleporter, teleportee, cause, target, delay);
|
||||
Bukkit.getServer().getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
delay = event.getDelay();
|
||||
|
||||
Trade cashCharge = chargeFor;
|
||||
|
||||
if (teleporter != null && chargeFor != null) {
|
||||
chargeFor.isAffordableFor(teleporter);
|
||||
|
||||
//This code is to make sure that commandcosts are checked in the initial world, and not in the resulting world.
|
||||
if (!chargeFor.getCommandCost(teleporter).equals(BigDecimal.ZERO)) {
|
||||
//By converting a command cost to a regular cost, the command cost permission isn't checked when executing the charge after teleport.
|
||||
cashCharge = new Trade(chargeFor.getCommandCost(teleporter), ess);
|
||||
}
|
||||
}
|
||||
|
||||
cooldown(true);
|
||||
if (delay <= 0 || teleporter == null
|
||||
|| teleporter.isAuthorized("essentials.teleport.timer.bypass")
|
||||
|| teleportOwner.isAuthorized("essentials.teleport.timer.bypass")
|
||||
|| teleportee.isAuthorized("essentials.teleport.timer.bypass")) {
|
||||
cooldown(false);
|
||||
now(teleportee, target, cause);
|
||||
if (teleporter != null && cashCharge != null) {
|
||||
cashCharge.charge(teleporter);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
cancel(false);
|
||||
warnUser(teleportee, delay);
|
||||
initTimer((long) (delay * 1000.0), teleportee, target, cashCharge, cause, false);
|
||||
}
|
||||
|
||||
//The respawn function is a wrapper used to handle tp fallback, on /jail and /home
|
||||
@Override
|
||||
@Deprecated
|
||||
public void respawn(final Trade chargeFor, final TeleportCause cause) throws Exception {
|
||||
double delay = ess.getSettings().getTeleportDelay();
|
||||
|
||||
final TeleportWarmupEvent event = new TeleportWarmupEvent(teleportOwner, cause, null, delay);
|
||||
Bukkit.getServer().getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
delay = event.getDelay();
|
||||
|
||||
if (chargeFor != null) {
|
||||
chargeFor.isAffordableFor(teleportOwner);
|
||||
}
|
||||
cooldown(true);
|
||||
if (delay <= 0 || teleportOwner.isAuthorized("essentials.teleport.timer.bypass")) {
|
||||
cooldown(false);
|
||||
respawnNow(teleportOwner, cause);
|
||||
if (chargeFor != null) {
|
||||
chargeFor.charge(teleportOwner);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
cancel(false);
|
||||
warnUser(teleportOwner, delay);
|
||||
initTimer((long) (delay * 1000.0), teleportOwner, null, chargeFor, cause, true);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
void respawnNow(final IUser teleportee, final TeleportCause cause) throws Exception {
|
||||
final Player player = teleportee.getBase();
|
||||
final Location bed = player.getBedSpawnLocation();
|
||||
if (bed != null) {
|
||||
now(teleportee, new LocationTarget(bed), cause);
|
||||
} else {
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().info("Could not find bed spawn, forcing respawn event.");
|
||||
}
|
||||
final PlayerRespawnEvent pre = new PlayerRespawnEvent(player, player.getWorld().getSpawnLocation(), false);
|
||||
ess.getServer().getPluginManager().callEvent(pre);
|
||||
now(teleportee, new LocationTarget(pre.getRespawnLocation()), cause);
|
||||
}
|
||||
}
|
||||
|
||||
//The warp function is a wrapper used to teleportPlayer a player to a /warp
|
||||
@Override
|
||||
@Deprecated
|
||||
public void warp(final IUser teleportee, String warp, final Trade chargeFor, final TeleportCause cause) throws Exception {
|
||||
final UserWarpEvent event = new UserWarpEvent(teleportee, warp, chargeFor);
|
||||
Bukkit.getServer().getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
warp = event.getWarp();
|
||||
final Location loc = ess.getWarps().getWarp(warp);
|
||||
teleportee.sendMessage(tl("warpingTo", warp, loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
|
||||
if (!teleportee.equals(teleportOwner)) {
|
||||
teleportOwner.sendMessage(tl("warpingTo", warp, loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
|
||||
}
|
||||
teleport(teleportee, new LocationTarget(loc), chargeFor, cause);
|
||||
}
|
||||
|
||||
//The back function is a wrapper used to teleportPlayer a player /back to their previous location.
|
||||
@Override
|
||||
@Deprecated
|
||||
public void back(final Trade chargeFor) throws Exception {
|
||||
back(teleportOwner, chargeFor);
|
||||
}
|
||||
|
||||
//This function is a wrapper over the other back function for cases where another player performs back for them
|
||||
@Override
|
||||
@Deprecated
|
||||
public void back(final IUser teleporter, final Trade chargeFor) throws Exception {
|
||||
tpType = TeleportType.BACK;
|
||||
final Location loc = teleportOwner.getLastLocation();
|
||||
teleportOwner.sendMessage(tl("backUsageMsg", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
|
||||
teleportOther(teleporter, teleportOwner, new LocationTarget(loc), chargeFor, TeleportCause.COMMAND);
|
||||
}
|
||||
|
||||
//This function is used to throw a user back after a jail sentence
|
||||
@Override
|
||||
@Deprecated
|
||||
public void back() throws Exception {
|
||||
now(teleportOwner, new LocationTarget(teleportOwner.getLastLocation()), TeleportCause.COMMAND);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setTpType(final TeleportType tpType) {
|
||||
this.tpType = tpType;
|
||||
}
|
||||
|
||||
//If we need to cancelTimer a pending teleportPlayer call this method
|
||||
@Deprecated
|
||||
private void cancel(final boolean notifyUser) {
|
||||
if (timedTeleport != null) {
|
||||
timedTeleport.cancelTimer(notifyUser);
|
||||
timedTeleport = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private void initTimer(final long delay, final IUser teleportUser, final ITarget target, final Trade chargeFor, final TeleportCause cause, final boolean respawn) {
|
||||
timedTeleport = new TimedTeleport(teleportOwner, ess, this, delay, teleportUser, target, chargeFor, cause, respawn);
|
||||
}
|
||||
|
||||
public enum TeleportType {
|
||||
TPA,
|
||||
BACK,
|
||||
NORMAL
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import net.ess3.api.IEssentials;
|
||||
import net.ess3.api.IUser;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
@Deprecated
|
||||
public class TimedTeleport implements Runnable {
|
||||
private static final double MOVE_CONSTANT = 0.3;
|
||||
private final IUser teleportOwner;
|
||||
private final IEssentials ess;
|
||||
private final Teleport teleport;
|
||||
private final UUID timer_teleportee;
|
||||
private final long timer_started; // time this task was initiated
|
||||
private final long timer_delay; // how long to delay the teleportPlayer
|
||||
// note that I initially stored a clone of the location for reference, but...
|
||||
// when comparing locations, I got incorrect mismatches (rounding errors, looked like)
|
||||
// so, the X/Y/Z values are stored instead and rounded off
|
||||
private final long timer_initX;
|
||||
private final long timer_initY;
|
||||
private final long timer_initZ;
|
||||
private final ITarget timer_teleportTarget;
|
||||
private final boolean timer_respawn;
|
||||
private final boolean timer_canMove;
|
||||
private final Trade timer_chargeFor;
|
||||
private final TeleportCause timer_cause;
|
||||
private int timer_task;
|
||||
private double timer_health;
|
||||
|
||||
TimedTeleport(final IUser user, final IEssentials ess, final Teleport teleport, final long delay, final IUser teleportUser, final ITarget target, final Trade chargeFor, final TeleportCause cause, final boolean respawn) {
|
||||
this.teleportOwner = user;
|
||||
this.ess = ess;
|
||||
this.teleport = teleport;
|
||||
this.timer_started = System.currentTimeMillis();
|
||||
this.timer_delay = delay;
|
||||
this.timer_health = teleportUser.getBase().getHealth();
|
||||
this.timer_initX = Math.round(teleportUser.getBase().getLocation().getX() * MOVE_CONSTANT);
|
||||
this.timer_initY = Math.round(teleportUser.getBase().getLocation().getY() * MOVE_CONSTANT);
|
||||
this.timer_initZ = Math.round(teleportUser.getBase().getLocation().getZ() * MOVE_CONSTANT);
|
||||
this.timer_teleportee = teleportUser.getBase().getUniqueId();
|
||||
this.timer_teleportTarget = target;
|
||||
this.timer_chargeFor = chargeFor;
|
||||
this.timer_cause = cause;
|
||||
this.timer_respawn = respawn;
|
||||
this.timer_canMove = user.isAuthorized("essentials.teleport.timer.move");
|
||||
|
||||
timer_task = ess.runTaskTimerAsynchronously(this, 20, 20).getTaskId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
if (teleportOwner == null || !teleportOwner.getBase().isOnline() || teleportOwner.getBase().getLocation() == null) {
|
||||
cancelTimer(false);
|
||||
return;
|
||||
}
|
||||
|
||||
final IUser teleportUser = ess.getUser(this.timer_teleportee);
|
||||
|
||||
if (teleportUser == null || !teleportUser.getBase().isOnline()) {
|
||||
cancelTimer(false);
|
||||
return;
|
||||
}
|
||||
|
||||
final Location currLocation = teleportUser.getBase().getLocation();
|
||||
if (currLocation == null) {
|
||||
cancelTimer(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!timer_canMove && (Math.round(currLocation.getX() * MOVE_CONSTANT) != timer_initX || Math.round(currLocation.getY() * MOVE_CONSTANT) != timer_initY || Math.round(currLocation.getZ() * MOVE_CONSTANT) != timer_initZ || teleportUser.getBase().getHealth() < timer_health)) {
|
||||
// user moved, cancelTimer teleportPlayer
|
||||
cancelTimer(true);
|
||||
return;
|
||||
}
|
||||
|
||||
class DelayedTeleportTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
timer_health = teleportUser.getBase().getHealth(); // in case user healed, then later gets injured
|
||||
final long now = System.currentTimeMillis();
|
||||
if (now > timer_started + timer_delay) {
|
||||
try {
|
||||
teleport.cooldown(false);
|
||||
} catch (final Exception ex) {
|
||||
teleportOwner.sendMessage(tl("cooldownWithMessage", ex.getMessage()));
|
||||
if (teleportOwner != teleportUser) {
|
||||
teleportUser.sendMessage(tl("cooldownWithMessage", ex.getMessage()));
|
||||
}
|
||||
}
|
||||
try {
|
||||
cancelTimer(false);
|
||||
teleportUser.sendMessage(tl("teleportationCommencing"));
|
||||
|
||||
if (timer_chargeFor != null) {
|
||||
timer_chargeFor.isAffordableFor(teleportOwner);
|
||||
}
|
||||
if (timer_respawn) {
|
||||
teleport.respawnNow(teleportUser, timer_cause);
|
||||
} else {
|
||||
teleport.now(teleportUser, timer_teleportTarget, timer_cause);
|
||||
}
|
||||
if (timer_chargeFor != null) {
|
||||
timer_chargeFor.charge(teleportOwner);
|
||||
}
|
||||
|
||||
} catch (final Exception ex) {
|
||||
ess.showError(teleportOwner.getSource(), ex, "\\ teleport");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ess.scheduleSyncDelayedTask(new DelayedTeleportTask());
|
||||
}
|
||||
|
||||
//If we need to cancelTimer a pending teleportPlayer call this method
|
||||
void cancelTimer(final boolean notifyUser) {
|
||||
if (timer_task == -1) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ess.getServer().getScheduler().cancelTask(timer_task);
|
||||
if (notifyUser) {
|
||||
teleportOwner.sendMessage(tl("pendingTeleportCancelled"));
|
||||
if (timer_teleportee != null && !timer_teleportee.equals(teleportOwner.getBase().getUniqueId())) {
|
||||
ess.getUser(timer_teleportee).sendMessage(tl("pendingTeleportCancelled"));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
timer_task = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentSkipListMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class UUIDMap {
|
||||
private static final ScheduledExecutorService writeScheduler = Executors.newScheduledThreadPool(1);
|
||||
private static boolean pendingWrite;
|
||||
private static boolean loading = false;
|
||||
private final transient net.ess3.api.IEssentials ess;
|
||||
private final File userList;
|
||||
private final transient Pattern splitPattern = Pattern.compile(",");
|
||||
private final Runnable writeTaskRunnable;
|
||||
|
||||
public UUIDMap(final net.ess3.api.IEssentials ess) {
|
||||
this.ess = ess;
|
||||
userList = new File(ess.getDataFolder(), "usermap.csv");
|
||||
pendingWrite = false;
|
||||
writeTaskRunnable = () -> {
|
||||
if (pendingWrite) {
|
||||
try {
|
||||
new WriteRunner(ess.getDataFolder(), userList, ess.getUserMap().getNames()).run();
|
||||
} catch (final Throwable t) { // bad code to prevent task from being suppressed
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
writeScheduler.scheduleWithFixedDelay(writeTaskRunnable, 5, 5, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public void loadAllUsers(final ConcurrentSkipListMap<String, UUID> names, final ConcurrentSkipListMap<UUID, ArrayList<String>> history) {
|
||||
try {
|
||||
if (!userList.exists()) {
|
||||
userList.createNewFile();
|
||||
}
|
||||
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().log(Level.INFO, "Reading usermap from disk");
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
names.clear();
|
||||
history.clear();
|
||||
loading = true;
|
||||
|
||||
try (final BufferedReader reader = new BufferedReader(new FileReader(userList))) {
|
||||
while (true) {
|
||||
final String line = reader.readLine();
|
||||
if (line == null) {
|
||||
break;
|
||||
} else {
|
||||
final String[] values = splitPattern.split(line);
|
||||
if (values.length == 2) {
|
||||
final String name = values[0];
|
||||
final UUID uuid = UUID.fromString(values[1]);
|
||||
names.put(name, uuid);
|
||||
if (!history.containsKey(uuid)) {
|
||||
final ArrayList<String> list = new ArrayList<>();
|
||||
list.add(name);
|
||||
history.put(uuid, list);
|
||||
} else {
|
||||
final ArrayList<String> list = history.get(uuid);
|
||||
if (!list.contains(name)) {
|
||||
list.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
loading = false;
|
||||
} catch (final IOException ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeUUIDMap() {
|
||||
pendingWrite = true;
|
||||
}
|
||||
|
||||
public void forceWriteUUIDMap() {
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().log(Level.INFO, "Forcing usermap write to disk");
|
||||
}
|
||||
pendingWrite = true;
|
||||
writeTaskRunnable.run();
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
writeScheduler.submit(writeTaskRunnable);
|
||||
writeScheduler.shutdown();
|
||||
}
|
||||
|
||||
private static final class WriteRunner implements Runnable {
|
||||
private final File location;
|
||||
private final File endFile;
|
||||
private final Map<String, UUID> names;
|
||||
|
||||
private WriteRunner(final File location, final File endFile, final Map<String, UUID> names) {
|
||||
this.location = location;
|
||||
this.endFile = endFile;
|
||||
this.names = new HashMap<>(names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
pendingWrite = false;
|
||||
if (loading || names.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
File configFile = null;
|
||||
|
||||
try {
|
||||
configFile = File.createTempFile("usermap", ".tmp.csv", location);
|
||||
|
||||
final BufferedWriter bWriter = new BufferedWriter(new FileWriter(configFile));
|
||||
for (final Map.Entry<String, UUID> entry : names.entrySet()) {
|
||||
bWriter.write(entry.getKey() + "," + entry.getValue().toString());
|
||||
bWriter.newLine();
|
||||
}
|
||||
|
||||
bWriter.close();
|
||||
Files.move(configFile, endFile);
|
||||
} catch (final IOException ex) {
|
||||
try {
|
||||
if (configFile != null && configFile.exists()) {
|
||||
Files.move(configFile, new File(endFile.getParentFile(), "usermap.bak.csv"));
|
||||
}
|
||||
} catch (final Exception ex2) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, ex2.getMessage(), ex2);
|
||||
}
|
||||
Bukkit.getLogger().log(Level.WARNING, ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,313 +0,0 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import com.earth2me.essentials.utils.StringUtil;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
import net.ess3.api.IEssentials;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentSkipListMap;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class UserMap extends CacheLoader<String, User> implements IConf {
|
||||
private static boolean legacy = false;
|
||||
private static Method getLegacy;
|
||||
private final transient IEssentials ess;
|
||||
private final transient ConcurrentSkipListSet<UUID> keys = new ConcurrentSkipListSet<>();
|
||||
private final transient ConcurrentSkipListMap<String, UUID> names = new ConcurrentSkipListMap<>();
|
||||
private final transient ConcurrentSkipListMap<UUID, ArrayList<String>> history = new ConcurrentSkipListMap<>();
|
||||
private final UUIDMap uuidMap;
|
||||
private final transient Cache<String, User> users;
|
||||
private final Pattern validUserPattern = Pattern.compile("^[a-zA-Z0-9_]{2,16}$");
|
||||
|
||||
public UserMap(final IEssentials ess) {
|
||||
super();
|
||||
this.ess = ess;
|
||||
uuidMap = new UUIDMap(ess);
|
||||
//RemovalListener<UUID, User> remListener = new UserMapRemovalListener();
|
||||
//users = CacheBuilder.newBuilder().maximumSize(ess.getSettings().getMaxUserCacheCount()).softValues().removalListener(remListener).build(this);
|
||||
final CacheBuilder<Object, Object> cacheBuilder = CacheBuilder.newBuilder();
|
||||
final int maxCount = ess.getSettings().getMaxUserCacheCount();
|
||||
try {
|
||||
cacheBuilder.maximumSize(maxCount);
|
||||
} catch (final NoSuchMethodError nsme) {
|
||||
legacy = true;
|
||||
legacyMaximumSize(cacheBuilder, maxCount);
|
||||
}
|
||||
cacheBuilder.softValues();
|
||||
if (!legacy) {
|
||||
users = cacheBuilder.build(this);
|
||||
} else {
|
||||
users = legacyBuild(cacheBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadAllUsersAsync(final IEssentials ess) {
|
||||
ess.runTaskAsynchronously(() -> {
|
||||
synchronized (users) {
|
||||
final File userdir = new File(ess.getDataFolder(), "userdata");
|
||||
if (!userdir.exists()) {
|
||||
return;
|
||||
}
|
||||
keys.clear();
|
||||
users.invalidateAll();
|
||||
for (final String string : userdir.list()) {
|
||||
if (!string.endsWith(".yml")) {
|
||||
continue;
|
||||
}
|
||||
final String name = string.substring(0, string.length() - 4);
|
||||
try {
|
||||
keys.add(UUID.fromString(name));
|
||||
} catch (final IllegalArgumentException ex) {
|
||||
//Ignore these users till they rejoin.
|
||||
}
|
||||
}
|
||||
uuidMap.loadAllUsers(names, history);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean userExists(final UUID uuid) {
|
||||
return keys.contains(uuid);
|
||||
}
|
||||
|
||||
public User getUser(final String name) {
|
||||
try {
|
||||
final String sanitizedName = StringUtil.safeString(name);
|
||||
if (names.containsKey(sanitizedName)) {
|
||||
final UUID uuid = names.get(sanitizedName);
|
||||
return getUser(uuid);
|
||||
}
|
||||
|
||||
final File userFile = getUserFileFromString(sanitizedName);
|
||||
if (userFile.exists()) {
|
||||
ess.getLogger().info("Importing user " + name + " to usermap.");
|
||||
final User user = new User(new OfflinePlayer(sanitizedName, ess.getServer()), ess);
|
||||
trackUUID(user.getBase().getUniqueId(), user.getName(), true);
|
||||
return user;
|
||||
}
|
||||
return null;
|
||||
} catch (final UncheckedExecutionException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public User getUser(final UUID uuid) {
|
||||
try {
|
||||
if (!legacy) {
|
||||
return ((LoadingCache<String, User>) users).get(uuid.toString());
|
||||
} else {
|
||||
return legacyCacheGet(uuid);
|
||||
}
|
||||
} catch (final ExecutionException | UncheckedExecutionException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void trackUUID(final UUID uuid, final String name, final boolean replace) {
|
||||
if (uuid != null) {
|
||||
keys.add(uuid);
|
||||
if (name != null && name.length() > 0) {
|
||||
final String keyName = ess.getSettings().isSafeUsermap() ? StringUtil.safeString(name) : name;
|
||||
if (!names.containsKey(keyName)) {
|
||||
names.put(keyName, uuid);
|
||||
uuidMap.writeUUIDMap();
|
||||
} else if (!names.get(keyName).equals(uuid)) {
|
||||
if (replace) {
|
||||
ess.getLogger().info("Found new UUID for " + name + ". Replacing " + names.get(keyName).toString() + " with " + uuid.toString());
|
||||
names.put(keyName, uuid);
|
||||
uuidMap.writeUUIDMap();
|
||||
} else {
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().info("Found old UUID for " + name + " (" + uuid.toString() + "). Not adding to usermap.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public User load(final String stringUUID) throws Exception {
|
||||
final UUID uuid = UUID.fromString(stringUUID);
|
||||
Player player = ess.getServer().getPlayer(uuid);
|
||||
if (player != null) {
|
||||
final User user = new User(player, ess);
|
||||
trackUUID(uuid, user.getName(), true);
|
||||
return user;
|
||||
}
|
||||
|
||||
final File userFile = getUserFileFromID(uuid);
|
||||
|
||||
if (userFile.exists()) {
|
||||
player = new OfflinePlayer(uuid, ess.getServer());
|
||||
final User user = new User(player, ess);
|
||||
((OfflinePlayer) player).setName(user.getLastAccountName());
|
||||
trackUUID(uuid, user.getName(), false);
|
||||
return user;
|
||||
}
|
||||
|
||||
throw new Exception("User not found!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadConfig() {
|
||||
getUUIDMap().forceWriteUUIDMap();
|
||||
loadAllUsersAsync(ess);
|
||||
}
|
||||
|
||||
public void invalidateAll() {
|
||||
users.invalidateAll();
|
||||
}
|
||||
|
||||
public void removeUser(final String name) {
|
||||
if (names == null) {
|
||||
ess.getLogger().warning("Name collection is null, cannot remove user.");
|
||||
return;
|
||||
}
|
||||
final UUID uuid = names.get(name);
|
||||
if (uuid != null) {
|
||||
keys.remove(uuid);
|
||||
users.invalidate(uuid.toString());
|
||||
}
|
||||
names.remove(name);
|
||||
names.remove(StringUtil.safeString(name));
|
||||
}
|
||||
|
||||
public Set<UUID> getAllUniqueUsers() {
|
||||
return Collections.unmodifiableSet(keys);
|
||||
}
|
||||
|
||||
public int getUniqueUsers() {
|
||||
return keys.size();
|
||||
}
|
||||
|
||||
protected ConcurrentSkipListMap<String, UUID> getNames() {
|
||||
return names;
|
||||
}
|
||||
|
||||
protected ConcurrentSkipListMap<UUID, ArrayList<String>> getHistory() {
|
||||
return history;
|
||||
}
|
||||
|
||||
public List<String> getUserHistory(final UUID uuid) {
|
||||
return history.get(uuid);
|
||||
}
|
||||
|
||||
public UUIDMap getUUIDMap() {
|
||||
return uuidMap;
|
||||
}
|
||||
// class UserMapRemovalListener implements RemovalListener
|
||||
// {
|
||||
// @Override
|
||||
// public void onRemoval(final RemovalNotification notification)
|
||||
// {
|
||||
// Object value = notification.getValue();
|
||||
// if (value != null)
|
||||
// {
|
||||
// ((User)value).cleanup();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
private File getUserFileFromID(final UUID uuid) {
|
||||
final File userFolder = new File(ess.getDataFolder(), "userdata");
|
||||
return new File(userFolder, uuid.toString() + ".yml");
|
||||
}
|
||||
|
||||
public File getUserFileFromString(final String name) {
|
||||
final File userFolder = new File(ess.getDataFolder(), "userdata");
|
||||
return new File(userFolder, StringUtil.sanitizeFileName(name) + ".yml");
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public User getUserFromBukkit(String name) {
|
||||
name = StringUtil.safeString(name);
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().warning("Using potentially blocking Bukkit UUID lookup for: " + name);
|
||||
}
|
||||
// Don't attempt to look up entirely invalid usernames
|
||||
if (name == null || !validUserPattern.matcher(name).matches()) {
|
||||
return null;
|
||||
}
|
||||
final org.bukkit.OfflinePlayer offlinePlayer = ess.getServer().getOfflinePlayer(name);
|
||||
if (offlinePlayer == null) {
|
||||
return null;
|
||||
}
|
||||
final UUID uuid;
|
||||
try {
|
||||
uuid = offlinePlayer.getUniqueId();
|
||||
} catch (final UnsupportedOperationException | NullPointerException e) {
|
||||
return null;
|
||||
}
|
||||
// This is how Bukkit generates fake UUIDs
|
||||
if (UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8)).equals(uuid)) {
|
||||
return null;
|
||||
} else {
|
||||
names.put(name, uuid);
|
||||
return getUser(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
private User legacyCacheGet(final UUID uuid) {
|
||||
if (getLegacy == null) {
|
||||
final Class<?> usersClass = users.getClass();
|
||||
for (final Method m : usersClass.getDeclaredMethods()) {
|
||||
if (m.getName().equals("get")) {
|
||||
getLegacy = m;
|
||||
getLegacy.setAccessible(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
return (User) getLegacy.invoke(users, uuid.toString());
|
||||
} catch (final IllegalAccessException | InvocationTargetException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void legacyMaximumSize(final CacheBuilder builder, final int maxCount) {
|
||||
try {
|
||||
final Method maxSizeLegacy = builder.getClass().getDeclaredMethod("maximumSize", Integer.TYPE);
|
||||
maxSizeLegacy.setAccessible(true);
|
||||
maxSizeLegacy.invoke(builder, maxCount);
|
||||
} catch (final NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Cache<String, User> legacyBuild(final CacheBuilder builder) {
|
||||
Method build = null;
|
||||
for (final Method method : builder.getClass().getDeclaredMethods()) {
|
||||
if (method.getName().equals("build")) {
|
||||
build = method;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Cache<String, User> legacyUsers;
|
||||
try {
|
||||
assert build != null;
|
||||
build.setAccessible(true);
|
||||
legacyUsers = (Cache<String, User>) build.invoke(builder, this);
|
||||
} catch (final IllegalAccessException | InvocationTargetException e) {
|
||||
legacyUsers = null;
|
||||
}
|
||||
return legacyUsers;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package com.earth2me.essentials.api;
|
||||
|
||||
/**
|
||||
* Represents a storage object that is reloadable.
|
||||
*
|
||||
* @deprecated This is a remnant of the abandoned 3.x storage system. Neither future 2.x code nor external plugins
|
||||
* should use this interface.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface IReload {
|
||||
/**
|
||||
* Reloads the given storage object.
|
||||
*/
|
||||
void onReload();
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.CommandSource;
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.textreader.SimpleTextInput;
|
||||
import com.earth2me.essentials.textreader.TextPager;
|
||||
import com.earth2me.essentials.utils.NumberUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.bukkit.Server;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Commandbalancetop extends EssentialsCommand {
|
||||
public static final int MINUSERS = 50;
|
||||
private static final int CACHETIME = 2 * 60 * 1000;
|
||||
private static final SimpleTextInput cache = new SimpleTextInput();
|
||||
private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
private static long cacheage = 0;
|
||||
|
||||
public Commandbalancetop() {
|
||||
super("balancetop");
|
||||
}
|
||||
|
||||
private static void outputCache(final CommandSource sender, final int page) {
|
||||
final Calendar cal = Calendar.getInstance();
|
||||
cal.setTimeInMillis(cacheage);
|
||||
final DateFormat format = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
|
||||
sender.sendMessage(tl("balanceTop", format.format(cal.getTime())));
|
||||
new TextPager(cache).showPage(Integer.toString(page), null, "balancetop", sender);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
int page = 0;
|
||||
boolean force = false;
|
||||
if (args.length > 0) {
|
||||
try {
|
||||
page = Integer.parseInt(args[0]);
|
||||
} catch (final NumberFormatException ex) {
|
||||
if (args[0].equalsIgnoreCase("force") && (!sender.isPlayer() || ess.getUser(sender.getPlayer()).isAuthorized("essentials.balancetop.force"))) {
|
||||
force = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!force && lock.readLock().tryLock()) {
|
||||
try {
|
||||
if (cacheage > System.currentTimeMillis() - CACHETIME) {
|
||||
outputCache(sender, page);
|
||||
return;
|
||||
}
|
||||
if (ess.getUserMap().getUniqueUsers() > MINUSERS) {
|
||||
sender.sendMessage(tl("orderBalances", ess.getUserMap().getUniqueUsers()));
|
||||
}
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
} else {
|
||||
if (ess.getUserMap().getUniqueUsers() > MINUSERS) {
|
||||
sender.sendMessage(tl("orderBalances", ess.getUserMap().getUniqueUsers()));
|
||||
}
|
||||
}
|
||||
ess.runTaskAsynchronously(new Viewer(sender, commandLabel, page, force));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
|
||||
if (args.length == 1) {
|
||||
final List<String> options = Lists.newArrayList("1");
|
||||
if (!sender.isPlayer() || ess.getUser(sender.getPlayer()).isAuthorized("essentials.balancetop.force")) {
|
||||
options.add("force");
|
||||
}
|
||||
return options;
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
private class Calculator implements Runnable {
|
||||
private final transient Viewer viewer;
|
||||
private final boolean force;
|
||||
|
||||
Calculator(final Viewer viewer, final boolean force) {
|
||||
this.viewer = viewer;
|
||||
this.force = force;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
if (force || cacheage <= System.currentTimeMillis() - CACHETIME) {
|
||||
cache.getLines().clear();
|
||||
final Map<String, BigDecimal> balances = new HashMap<>();
|
||||
BigDecimal totalMoney = BigDecimal.ZERO;
|
||||
if (ess.getSettings().isEcoDisabled()) {
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().info("Internal economy functions disabled, aborting baltop.");
|
||||
}
|
||||
} else {
|
||||
for (final UUID u : ess.getUserMap().getAllUniqueUsers()) {
|
||||
final User user = ess.getUserMap().getUser(u);
|
||||
if (user != null) {
|
||||
if (!ess.getSettings().isNpcsInBalanceRanking() && user.isNPC()) {
|
||||
// Don't list NPCs in output
|
||||
continue;
|
||||
}
|
||||
if (!user.isAuthorized("essentials.balancetop.exclude")) {
|
||||
final BigDecimal userMoney = user.getMoney();
|
||||
user.updateMoneyCache(userMoney);
|
||||
totalMoney = totalMoney.add(userMoney);
|
||||
final String name = user.isHidden() ? user.getName() : user.getDisplayName();
|
||||
balances.put(name, userMoney);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final List<Map.Entry<String, BigDecimal>> sortedEntries = new ArrayList<>(balances.entrySet());
|
||||
sortedEntries.sort((entry1, entry2) -> entry2.getValue().compareTo(entry1.getValue()));
|
||||
|
||||
cache.getLines().add(tl("serverTotal", NumberUtil.displayCurrency(totalMoney, ess)));
|
||||
int pos = 1;
|
||||
for (final Map.Entry<String, BigDecimal> entry : sortedEntries) {
|
||||
cache.getLines().add(tl("balanceTopLine", pos, entry.getKey(), NumberUtil.displayCurrency(entry.getValue(), ess)));
|
||||
pos++;
|
||||
}
|
||||
cacheage = System.currentTimeMillis();
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
ess.runTaskAsynchronously(viewer);
|
||||
}
|
||||
}
|
||||
|
||||
private class Viewer implements Runnable {
|
||||
private final transient CommandSource sender;
|
||||
private final transient int page;
|
||||
private final transient boolean force;
|
||||
private final transient String commandLabel;
|
||||
|
||||
Viewer(final CommandSource sender, final String commandLabel, final int page, final boolean force) {
|
||||
this.sender = sender;
|
||||
this.page = page;
|
||||
this.force = force;
|
||||
this.commandLabel = commandLabel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
if (!force && cacheage > System.currentTimeMillis() - CACHETIME) {
|
||||
outputCache(sender, page);
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
ess.runTaskAsynchronously(new Calculator(new Viewer(sender, commandLabel, page, false), force));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.CommandSource;
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.utils.DateUtil;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.MemoryConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConstructor;
|
||||
import org.bukkit.configuration.file.YamlRepresenter;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Commandcreatekit extends EssentialsCommand {
|
||||
|
||||
private static final String PASTE_URL = "https://hastebin.com/";
|
||||
private static final String PASTE_UPLOAD_URL = PASTE_URL + "documents";
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
|
||||
private final DumperOptions yamlOptions = new DumperOptions();
|
||||
private final Representer yamlRepresenter = new YamlRepresenter();
|
||||
private final YamlConstructor yamlConstructor = new YamlConstructor();
|
||||
|
||||
public Commandcreatekit() {
|
||||
super("createkit");
|
||||
yamlOptions.setDefaultFlowStyle(FlowStyle.BLOCK);
|
||||
yamlRepresenter.setDefaultFlowStyle(FlowStyle.BLOCK);
|
||||
}
|
||||
|
||||
// /createkit <name> <delay>
|
||||
@Override
|
||||
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
|
||||
if (args.length != 2) {
|
||||
throw new NotEnoughArgumentsException();
|
||||
}
|
||||
|
||||
// Command handler will auto fail if this fails.
|
||||
final long delay = Long.parseLong(args[1]);
|
||||
final String kitname = args[0];
|
||||
final ItemStack[] items = user.getBase().getInventory().getContents();
|
||||
final List<String> list = new ArrayList<>();
|
||||
for (final ItemStack is : items) {
|
||||
if (is != null && is.getType() != null && is.getType() != Material.AIR) {
|
||||
final String serialized = ess.getItemDb().serialize(is);
|
||||
list.add(serialized);
|
||||
}
|
||||
}
|
||||
// Some users might want to directly write to config knowing the consequences. *shrug*
|
||||
if (!ess.getSettings().isPastebinCreateKit()) {
|
||||
ess.getKits().addKit(kitname, list, delay);
|
||||
user.sendMessage(tl("createdKit", kitname, list.size(), delay));
|
||||
} else {
|
||||
final ConfigurationSection config = new MemoryConfiguration();
|
||||
config.set("kits." + kitname + ".delay", delay);
|
||||
config.set("kits." + kitname + ".items", list);
|
||||
|
||||
final Yaml yaml = new Yaml(yamlConstructor, yamlRepresenter, yamlOptions);
|
||||
String fileContents = "# Copy the kit code below into the kits section in your config.yml file\n";
|
||||
fileContents += yaml.dump(config.getValues(false));
|
||||
|
||||
uploadPaste(user.getSource(), kitname, delay, fileContents);
|
||||
}
|
||||
}
|
||||
|
||||
private void uploadPaste(final CommandSource sender, final String kitName, final long delay, final String contents) {
|
||||
executorService.submit(() -> {
|
||||
try {
|
||||
final HttpURLConnection connection = (HttpURLConnection) new URL(PASTE_UPLOAD_URL).openConnection();
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setDoInput(true);
|
||||
connection.setDoOutput(true);
|
||||
connection.setRequestProperty("User-Agent", "EssentialsX plugin");
|
||||
try (final OutputStream os = connection.getOutputStream()) {
|
||||
os.write(contents.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
// Error
|
||||
if (connection.getResponseCode() >= 400) {
|
||||
sender.sendMessage(tl("createKitFailed", kitName));
|
||||
final String message = CharStreams.toString(new InputStreamReader(connection.getErrorStream(), Charsets.UTF_8));
|
||||
ess.getLogger().severe("Error creating kit: " + message);
|
||||
return;
|
||||
}
|
||||
|
||||
// Read URL
|
||||
final JsonObject object = GSON.fromJson(new InputStreamReader(connection.getInputStream(), Charsets.UTF_8), JsonObject.class);
|
||||
final String pasteUrl = PASTE_URL + object.get("key").getAsString();
|
||||
connection.disconnect();
|
||||
|
||||
final String separator = tl("createKitSeparator");
|
||||
String delayFormat = "0";
|
||||
if (delay > 0) {
|
||||
delayFormat = DateUtil.formatDateDiff(System.currentTimeMillis() + (delay * 1000));
|
||||
}
|
||||
sender.sendMessage(separator);
|
||||
sender.sendMessage(tl("createKitSuccess", kitName, delayFormat, pasteUrl));
|
||||
sender.sendMessage(separator);
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().info(sender.getSender().getName() + " created a kit: " + pasteUrl);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
sender.sendMessage(tl("createKitFailed", kitName));
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.CommandSource;
|
||||
import org.bukkit.Server;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Commanddelwarp extends EssentialsCommand {
|
||||
public Commanddelwarp() {
|
||||
super("delwarp");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
if (args.length == 0) {
|
||||
throw new NotEnoughArgumentsException();
|
||||
}
|
||||
|
||||
ess.getWarps().removeWarp(args[0]);
|
||||
sender.sendMessage(tl("deleteWarp", args[0]));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
|
||||
if (args.length == 1) {
|
||||
return new ArrayList<>(ess.getWarps().getList());
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.utils.FormatUtil;
|
||||
import com.earth2me.essentials.utils.NumberUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.Sign;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Commandeditsign extends EssentialsCommand {
|
||||
public Commandeditsign() {
|
||||
super("editsign");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
|
||||
if (args.length == 0 || (args.length > 1 && !NumberUtil.isInt(args[1]))) {
|
||||
throw new NotEnoughArgumentsException();
|
||||
}
|
||||
|
||||
final Block target = user.getTargetBlock(5); //5 is a good number
|
||||
if (!(target.getState() instanceof Sign)) {
|
||||
throw new Exception(tl("editsignCommandTarget"));
|
||||
}
|
||||
final Sign sign = (Sign) target.getState();
|
||||
try {
|
||||
if (args[0].equalsIgnoreCase("set") && args.length > 2) {
|
||||
final int line = Integer.parseInt(args[1]) - 1;
|
||||
final String text = FormatUtil.formatString(user, "essentials.editsign", getFinalArg(args, 2)).trim();
|
||||
if (ChatColor.stripColor(text).length() > 15 && !user.isAuthorized("essentials.editsign.unlimited")) {
|
||||
throw new Exception(tl("editsignCommandLimit"));
|
||||
}
|
||||
sign.setLine(line, text);
|
||||
sign.update();
|
||||
user.sendMessage(tl("editsignCommandSetSuccess", line + 1, text));
|
||||
} else if (args[0].equalsIgnoreCase("clear")) {
|
||||
if (args.length == 1) {
|
||||
for (int i = 0; i < 4; i++) { // A whole one line of line savings!
|
||||
sign.setLine(i, "");
|
||||
}
|
||||
sign.update();
|
||||
user.sendMessage(tl("editsignCommandClear"));
|
||||
} else {
|
||||
final int line = Integer.parseInt(args[1]) - 1;
|
||||
sign.setLine(line, "");
|
||||
sign.update();
|
||||
user.sendMessage(tl("editsignCommandClearLine", line + 1));
|
||||
}
|
||||
} else {
|
||||
throw new NotEnoughArgumentsException();
|
||||
}
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
throw new Exception(tl("editsignCommandNoLine"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) {
|
||||
if (args.length == 1) {
|
||||
return Lists.newArrayList("set", "clear");
|
||||
} else if (args.length == 2) {
|
||||
return Lists.newArrayList("1", "2", "3", "4");
|
||||
} else if (args.length == 3 && args[0].equalsIgnoreCase("set") && NumberUtil.isPositiveInt(args[1])) {
|
||||
final int line = Integer.parseInt(args[1]);
|
||||
final Block target = user.getTargetBlock(5);
|
||||
if (target.getState() instanceof Sign && line <= 4) {
|
||||
final Sign sign = (Sign) target.getState();
|
||||
return Lists.newArrayList(FormatUtil.unformatString(user, "essentials.editsign", sign.getLine(line - 1)));
|
||||
}
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,492 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.CommandSource;
|
||||
import com.earth2me.essentials.EssentialsUpgrade;
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.UserMap;
|
||||
import com.earth2me.essentials.utils.DateUtil;
|
||||
import com.earth2me.essentials.utils.EnumUtil;
|
||||
import com.earth2me.essentials.utils.FloatUtil;
|
||||
import com.earth2me.essentials.utils.NumberUtil;
|
||||
import com.earth2me.essentials.utils.VersionUtil;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginDescriptionFile;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
// This command has 4 undocumented behaviours #EasterEgg
|
||||
public class Commandessentials extends EssentialsCommand {
|
||||
|
||||
private static final Sound NOTE_HARP = EnumUtil.valueOf(Sound.class, "BLOCK_NOTE_BLOCK_HARP", "BLOCK_NOTE_HARP", "NOTE_PIANO");
|
||||
private static final Sound MOO_SOUND = EnumUtil.valueOf(Sound.class, "COW_IDLE", "ENTITY_COW_MILK");
|
||||
|
||||
private static final String NYAN_TUNE = "1D#,1E,2F#,,2A#,1E,1D#,1E,2F#,2B,2D#,2E,2D#,2A#,2B,,2F#,,1D#,1E,2F#,2B,2C#,2A#,2B,2C#,2E,2D#,2E,2C#,,2F#,,2G#,,1D,1D#,,1C#,1D,1C#,1B,,1B,,1C#,,1D,,1D,1C#,1B,1C#,1D#,2F#,2G#,1D#,2F#,1C#,1D#,1B,1C#,1B,1D#,,2F#,,2G#,1D#,2F#,1C#,1D#,1B,1D,1D#,1D,1C#,1B,1C#,1D,,1B,1C#,1D#,2F#,1C#,1D,1C#,1B,1C#,,1B,,1C#,,2F#,,2G#,,1D,1D#,,1C#,1D,1C#,1B,,1B,,1C#,,1D,,1D,1C#,1B,1C#,1D#,2F#,2G#,1D#,2F#,1C#,1D#,1B,1C#,1B,1D#,,2F#,,2G#,1D#,2F#,1C#,1D#,1B,1D,1D#,1D,1C#,1B,1C#,1D,,1B,1C#,1D#,2F#,1C#,1D,1C#,1B,1C#,,1B,,1B,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1A#,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1F#,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1A#,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1F#,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1A#,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1F#,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1A#,,1B,,1F#,1G#,1B,,1F#,1G#,1B,1C#,1D#,1B,1E,1D#,1E,2F#,1B,,1B,,1F#,1G#,1B,1F#,1E,1D#,1C#,1B,,,,1F#,1B,,1F#,1G#,1B,,1F#,1G#,1B,1B,1C#,1D#,1B,1F#,1G#,1F#,1B,,1B,1A#,1B,1F#,1G#,1B,1E,1D#,1E,2F#,1B,,1B,,";
|
||||
private static final String[] CONSOLE_MOO = new String[] {" (__)", " (oo)", " /------\\/", " / | ||", " * /\\---/\\", " ~~ ~~", "....\"Have you mooed today?\"..."};
|
||||
private static final String[] PLAYER_MOO = new String[] {" (__)", " (oo)", " /------\\/", " / | | |", " * /\\---/\\", " ~~ ~~", "....\"Have you mooed today?\"..."};
|
||||
private static final List<String> versionPlugins = Arrays.asList(
|
||||
"Vault", // API
|
||||
"Reserve", // API
|
||||
"PlaceholderAPI", // API
|
||||
"CMI", // potential for issues
|
||||
"Towny", // past issues; admins should ensure latest
|
||||
"ChestShop", // past issues; admins should ensure latest
|
||||
"Citizens", // fires player events
|
||||
"LuckPerms", // permissions (recommended)
|
||||
"UltraPermissions",
|
||||
"PermissionsEx", // permissions (unsupported)
|
||||
"GroupManager", // permissions (unsupported)
|
||||
"bPermissions" // permissions (unsupported)
|
||||
);
|
||||
private static final List<String> officialPlugins = Arrays.asList(
|
||||
"EssentialsAntiBuild",
|
||||
"EssentialsChat",
|
||||
"EssentialsGeoIP",
|
||||
"EssentialsProtect",
|
||||
"EssentialsSpawn",
|
||||
"EssentialsXMPP"
|
||||
);
|
||||
private static final List<String> warnPlugins = Arrays.asList(
|
||||
"PermissionsEx",
|
||||
"GroupManager",
|
||||
"bPremissions"
|
||||
);
|
||||
private transient TuneRunnable currentTune = null;
|
||||
|
||||
public Commandessentials() {
|
||||
super("essentials");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
if (args.length == 0) {
|
||||
showUsage(sender);
|
||||
}
|
||||
|
||||
switch (args[0]) {
|
||||
// Info commands
|
||||
case "debug":
|
||||
case "verbose":
|
||||
runDebug(server, sender, commandLabel, args);
|
||||
break;
|
||||
|
||||
case "ver":
|
||||
case "version":
|
||||
runVersion(server, sender, commandLabel, args);
|
||||
break;
|
||||
|
||||
case "cmd":
|
||||
case "commands":
|
||||
runCommands(server, sender, commandLabel, args);
|
||||
break;
|
||||
|
||||
// Data commands
|
||||
case "reload":
|
||||
runReload(server, sender, commandLabel, args);
|
||||
break;
|
||||
case "reset":
|
||||
runReset(server, sender, commandLabel, args);
|
||||
break;
|
||||
case "cleanup":
|
||||
runCleanup(server, sender, commandLabel, args);
|
||||
break;
|
||||
case "uuidconvert":
|
||||
runUUIDConvert(server, sender, commandLabel, args);
|
||||
break;
|
||||
case "uuidtest":
|
||||
runUUIDTest(server, sender, commandLabel, args);
|
||||
break;
|
||||
|
||||
// "#EasterEgg"
|
||||
case "nya":
|
||||
case "nyan":
|
||||
runNya(server, sender, commandLabel, args);
|
||||
break;
|
||||
case "moo":
|
||||
runMoo(server, sender, commandLabel, args);
|
||||
break;
|
||||
default:
|
||||
showUsage(sender);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Displays the command's usage.
|
||||
private void showUsage(final CommandSource sender) throws Exception {
|
||||
throw new NotEnoughArgumentsException("/<command> <reload/debug/commands>");
|
||||
}
|
||||
|
||||
// Lists commands that are being handed over to other plugins.
|
||||
private void runCommands(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
|
||||
final StringBuilder disabledCommands = new StringBuilder();
|
||||
for (final Map.Entry<String, String> entry : ess.getAlternativeCommandsHandler().disabledCommands().entrySet()) {
|
||||
if (disabledCommands.length() > 0) {
|
||||
disabledCommands.append("\n");
|
||||
}
|
||||
disabledCommands.append(entry.getKey()).append(" => ").append(entry.getValue());
|
||||
}
|
||||
if (disabledCommands.length() > 0) {
|
||||
sender.sendMessage(tl("blockList"));
|
||||
sender.sendMessage(disabledCommands.toString());
|
||||
} else {
|
||||
sender.sendMessage(tl("blockListEmpty"));
|
||||
}
|
||||
}
|
||||
|
||||
// Resets the given player's user data.
|
||||
private void runReset(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
if (args.length < 2) {
|
||||
throw new Exception("/<command> reset <player>");
|
||||
}
|
||||
final User user = getPlayer(server, args, 1, true, true);
|
||||
user.reset();
|
||||
sender.sendMessage("Reset Essentials userdata for player: " + user.getDisplayName());
|
||||
}
|
||||
|
||||
// Toggles debug mode.
|
||||
private void runDebug(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
ess.getSettings().setDebug(!ess.getSettings().isDebug());
|
||||
sender.sendMessage("Essentials " + ess.getDescription().getVersion() + " debug mode " + (ess.getSettings().isDebug() ? "enabled" : "disabled"));
|
||||
}
|
||||
|
||||
// Reloads all reloadable configs.
|
||||
private void runReload(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
ess.reload();
|
||||
sender.sendMessage(tl("essentialsReload", ess.getDescription().getVersion()));
|
||||
}
|
||||
|
||||
// Pop tarts.
|
||||
private void runNya(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
if (currentTune != null) {
|
||||
currentTune.cancel();
|
||||
}
|
||||
|
||||
currentTune = new TuneRunnable(NYAN_TUNE, NOTE_HARP, ess::getOnlinePlayers);
|
||||
currentTune.runTaskTimer(ess, 20, 2);
|
||||
}
|
||||
|
||||
// Cow farts.
|
||||
private void runMoo(final Server server, final CommandSource sender, final String command, final String[] args) {
|
||||
if (args.length == 2 && args[1].equals("moo")) {
|
||||
for (final String s : CONSOLE_MOO) {
|
||||
logger.info(s);
|
||||
}
|
||||
for (final Player player : ess.getOnlinePlayers()) {
|
||||
player.sendMessage(PLAYER_MOO);
|
||||
player.playSound(player.getLocation(), MOO_SOUND, 1, 1.0f);
|
||||
}
|
||||
} else {
|
||||
if (sender.isPlayer()) {
|
||||
sender.getSender().sendMessage(PLAYER_MOO);
|
||||
final Player player = sender.getPlayer();
|
||||
player.playSound(player.getLocation(), MOO_SOUND, 1, 1.0f);
|
||||
|
||||
} else {
|
||||
sender.getSender().sendMessage(CONSOLE_MOO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cleans up inactive users.
|
||||
private void runCleanup(final Server server, final CommandSource sender, final String command, final String[] args) throws Exception {
|
||||
if (args.length < 2 || !NumberUtil.isInt(args[1])) {
|
||||
sender.sendMessage("This sub-command will delete users who haven't logged in in the last <days> days.");
|
||||
sender.sendMessage("Optional parameters define the minimum amount required to prevent deletion.");
|
||||
sender.sendMessage("Unless you define larger default values, this command will ignore people who have more than 0 money/homes.");
|
||||
throw new Exception("/<command> cleanup <days> [money] [homes]");
|
||||
}
|
||||
|
||||
sender.sendMessage(tl("cleaning"));
|
||||
|
||||
final long daysArg = Long.parseLong(args[1]);
|
||||
final double moneyArg = args.length >= 3 ? FloatUtil.parseDouble(args[2].replaceAll("[^0-9\\.]", "")) : 0;
|
||||
final int homesArg = args.length >= 4 && NumberUtil.isInt(args[3]) ? Integer.parseInt(args[3]) : 0;
|
||||
final UserMap userMap = ess.getUserMap();
|
||||
|
||||
ess.runTaskAsynchronously(() -> {
|
||||
final long currTime = System.currentTimeMillis();
|
||||
for (final UUID u : userMap.getAllUniqueUsers()) {
|
||||
final User user = ess.getUserMap().getUser(u);
|
||||
if (user == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
long lastLog = user.getLastLogout();
|
||||
if (lastLog == 0) {
|
||||
lastLog = user.getLastLogin();
|
||||
}
|
||||
if (lastLog == 0) {
|
||||
user.setLastLogin(currTime);
|
||||
}
|
||||
|
||||
if (user.isNPC()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final long timeDiff = currTime - lastLog;
|
||||
final long milliDays = daysArg * 24L * 60L * 60L * 1000L;
|
||||
final int homeCount = user.getHomes().size();
|
||||
final double moneyCount = user.getMoney().doubleValue();
|
||||
|
||||
if ((lastLog == 0) || (timeDiff < milliDays) || (homeCount > homesArg) || (moneyCount > moneyArg)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().info("Deleting user: " + user.getName() + " Money: " + moneyCount + " Homes: " + homeCount + " Last seen: " + DateUtil.formatDateDiff(lastLog));
|
||||
}
|
||||
|
||||
user.reset();
|
||||
}
|
||||
sender.sendMessage(tl("cleaned"));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Forces a rerun of userdata UUID conversion.
|
||||
private void runUUIDConvert(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
sender.sendMessage("Starting Essentials UUID userdata conversion; this may lag the server.");
|
||||
|
||||
final Boolean ignoreUFCache = args.length > 2 && args[1].toLowerCase(Locale.ENGLISH).contains("ignore");
|
||||
EssentialsUpgrade.uuidFileConvert(ess, ignoreUFCache);
|
||||
|
||||
sender.sendMessage("UUID conversion complete. Check your server log for more information.");
|
||||
}
|
||||
|
||||
// Looks up various UUIDs for a user.
|
||||
private void runUUIDTest(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
if (args.length < 2) {
|
||||
throw new Exception("/<command> uuidtest <name>");
|
||||
}
|
||||
final String name = args[1];
|
||||
sender.sendMessage("Looking up UUID for " + name);
|
||||
|
||||
UUID onlineUUID = null;
|
||||
|
||||
for (final Player player : ess.getOnlinePlayers()) {
|
||||
if (player.getName().equalsIgnoreCase(name)) {
|
||||
onlineUUID = player.getUniqueId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final UUID essUUID = ess.getUserMap().getUser(name).getConfigUUID();
|
||||
|
||||
final org.bukkit.OfflinePlayer player = ess.getServer().getOfflinePlayer(name);
|
||||
final UUID bukkituuid = player.getUniqueId();
|
||||
sender.sendMessage("Bukkit Lookup: " + bukkituuid.toString());
|
||||
|
||||
if (onlineUUID != null && onlineUUID != bukkituuid) {
|
||||
sender.sendMessage("Online player: " + onlineUUID.toString());
|
||||
}
|
||||
|
||||
if (essUUID != null && essUUID != bukkituuid) {
|
||||
sender.sendMessage("Essentials config: " + essUUID.toString());
|
||||
}
|
||||
|
||||
final UUID npcuuid = UUID.nameUUIDFromBytes(("NPC:" + name).getBytes(Charsets.UTF_8));
|
||||
sender.sendMessage("NPC UUID: " + npcuuid.toString());
|
||||
|
||||
final UUID offlineuuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8));
|
||||
sender.sendMessage("Offline Mode UUID: " + offlineuuid.toString());
|
||||
}
|
||||
|
||||
// Displays versions of EssentialsX and related plugins.
|
||||
private void runVersion(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
if (sender.isPlayer() && !ess.getUser(sender.getPlayer()).isAuthorized("essentials.version")) return;
|
||||
|
||||
boolean isMismatched = false;
|
||||
boolean isVaultInstalled = false;
|
||||
boolean isUnsupported = false;
|
||||
final VersionUtil.SupportStatus supportStatus = VersionUtil.getServerSupportStatus();
|
||||
final PluginManager pm = server.getPluginManager();
|
||||
final String essVer = pm.getPlugin("Essentials").getDescription().getVersion();
|
||||
|
||||
final String serverMessageKey;
|
||||
if (supportStatus.isSupported()) {
|
||||
serverMessageKey = "versionOutputFine";
|
||||
} else if (supportStatus == VersionUtil.SupportStatus.UNSTABLE) {
|
||||
serverMessageKey = "versionOutputUnsupported";
|
||||
} else {
|
||||
serverMessageKey = "versionOutputWarn";
|
||||
}
|
||||
|
||||
sender.sendMessage(tl(serverMessageKey, "Server", server.getBukkitVersion() + " " + server.getVersion()));
|
||||
sender.sendMessage(tl("versionOutputFine", "EssentialsX", essVer));
|
||||
|
||||
for (final Plugin plugin : pm.getPlugins()) {
|
||||
final PluginDescriptionFile desc = plugin.getDescription();
|
||||
String name = desc.getName();
|
||||
final String version = desc.getVersion();
|
||||
|
||||
if (name.startsWith("Essentials") && !name.equalsIgnoreCase("Essentials")) {
|
||||
if (officialPlugins.contains(name)) {
|
||||
name = name.replace("Essentials", "EssentialsX");
|
||||
|
||||
if (!version.equalsIgnoreCase(essVer)) {
|
||||
isMismatched = true;
|
||||
sender.sendMessage(tl("versionOutputWarn", name, version));
|
||||
} else {
|
||||
sender.sendMessage(tl("versionOutputFine", name, version));
|
||||
}
|
||||
} else {
|
||||
sender.sendMessage(tl("versionOutputUnsupported", name, version));
|
||||
isUnsupported = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (versionPlugins.contains(name)) {
|
||||
if (warnPlugins.contains(name)) {
|
||||
sender.sendMessage(tl("versionOutputUnsupported", name, version));
|
||||
isUnsupported = true;
|
||||
} else {
|
||||
sender.sendMessage(tl("versionOutputFine", name, version));
|
||||
}
|
||||
}
|
||||
|
||||
if (name.equals("Vault")) isVaultInstalled = true;
|
||||
}
|
||||
|
||||
if (isMismatched) {
|
||||
sender.sendMessage(tl("versionMismatchAll"));
|
||||
}
|
||||
|
||||
if (!isVaultInstalled) {
|
||||
sender.sendMessage(tl("versionOutputVaultMissing"));
|
||||
}
|
||||
|
||||
if (isUnsupported) {
|
||||
sender.sendMessage(tl("versionOutputUnsupportedPlugins"));
|
||||
}
|
||||
|
||||
switch (supportStatus) {
|
||||
case UNSTABLE:
|
||||
sender.sendMessage(tl("serverUnsupportedMods"));
|
||||
break;
|
||||
case OUTDATED:
|
||||
sender.sendMessage(tl("serverUnsupported"));
|
||||
break;
|
||||
case LIMITED:
|
||||
sender.sendMessage(tl("serverUnsupportedLimitedApi"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
|
||||
if (args.length == 1) {
|
||||
final List<String> options = Lists.newArrayList();
|
||||
options.add("debug");
|
||||
options.add("commands");
|
||||
options.add("version");
|
||||
options.add("reload");
|
||||
options.add("reset");
|
||||
options.add("cleanup");
|
||||
//options.add("uuidconvert");
|
||||
//options.add("uuidtest");
|
||||
//options.add("nya");
|
||||
//options.add("moo");
|
||||
return options;
|
||||
}
|
||||
|
||||
switch (args[0]) {
|
||||
case "moo":
|
||||
if (args.length == 2) {
|
||||
return Lists.newArrayList("moo");
|
||||
}
|
||||
break;
|
||||
case "reset":
|
||||
case "uuidtest":
|
||||
if (args.length == 2) {
|
||||
return getPlayers(server, sender);
|
||||
}
|
||||
break;
|
||||
case "cleanup":
|
||||
if (args.length == 2) {
|
||||
return COMMON_DURATIONS;
|
||||
} else if (args.length == 3 || args.length == 4) {
|
||||
return Lists.newArrayList("-1", "0");
|
||||
}
|
||||
break;
|
||||
case "uuidconvert":
|
||||
if (args.length == 2) {
|
||||
return Lists.newArrayList("ignoreUFCache");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static class TuneRunnable extends BukkitRunnable {
|
||||
private static final Map<String, Float> noteMap = ImmutableMap.<String, Float>builder()
|
||||
.put("1F#", 0.5f)
|
||||
.put("1G", 0.53f)
|
||||
.put("1G#", 0.56f)
|
||||
.put("1A", 0.6f)
|
||||
.put("1A#", 0.63f)
|
||||
.put("1B", 0.67f)
|
||||
.put("1C", 0.7f)
|
||||
.put("1C#", 0.76f)
|
||||
.put("1D", 0.8f)
|
||||
.put("1D#", 0.84f)
|
||||
.put("1E", 0.9f)
|
||||
.put("1F", 0.94f)
|
||||
.put("2F#", 1.0f)
|
||||
.put("2G", 1.06f)
|
||||
.put("2G#", 1.12f)
|
||||
.put("2A", 1.18f)
|
||||
.put("2A#", 1.26f)
|
||||
.put("2B", 1.34f)
|
||||
.put("2C", 1.42f)
|
||||
.put("2C#", 1.5f)
|
||||
.put("2D", 1.6f)
|
||||
.put("2D#", 1.68f)
|
||||
.put("2E", 1.78f)
|
||||
.put("2F", 1.88f)
|
||||
.build();
|
||||
|
||||
private final String[] tune;
|
||||
private final Sound sound;
|
||||
private final Supplier<Collection<Player>> players;
|
||||
private int i = 0;
|
||||
|
||||
TuneRunnable(final String tuneStr, final Sound sound, final Supplier<Collection<Player>> players) {
|
||||
this.tune = tuneStr.split(",");
|
||||
this.sound = sound;
|
||||
this.players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final String note = tune[i];
|
||||
i++;
|
||||
if (i >= tune.length) {
|
||||
cancel();
|
||||
}
|
||||
if (note == null || note.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final Player onlinePlayer : players.get()) {
|
||||
onlinePlayer.playSound(onlinePlayer.getLocation(), sound, 1, noteMap.get(note));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.CommandSource;
|
||||
import com.earth2me.essentials.Console;
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.utils.FormatUtil;
|
||||
import org.bukkit.Server;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Commandhelpop extends EssentialsCommand {
|
||||
public Commandhelpop() {
|
||||
super("helpop");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
|
||||
user.setDisplayNick();
|
||||
final String message = sendMessage(server, user.getDisplayName(), args);
|
||||
if (!user.isAuthorized("essentials.helpop.receive")) {
|
||||
user.sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
sendMessage(server, Console.NAME, args);
|
||||
}
|
||||
|
||||
private String sendMessage(final Server server, final String from, final String[] args) throws Exception {
|
||||
if (args.length < 1) {
|
||||
throw new NotEnoughArgumentsException();
|
||||
}
|
||||
final String message = tl("helpOp", from, FormatUtil.stripFormat(getFinalArg(args, 0)));
|
||||
server.getLogger().log(Level.INFO, message);
|
||||
ess.broadcastMessage("essentials.helpop.receive", message);
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.utils.FormatUtil;
|
||||
import com.earth2me.essentials.utils.NumberUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Commanditemlore extends EssentialsCommand {
|
||||
|
||||
public Commanditemlore() {
|
||||
super("itemlore");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
|
||||
final ItemStack item = user.getBase().getItemInHand();
|
||||
if (item.getType().name().contains("AIR")) {
|
||||
throw new Exception(tl("itemloreInvalidItem"));
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
throw new NotEnoughArgumentsException();
|
||||
}
|
||||
|
||||
final ItemMeta im = item.getItemMeta();
|
||||
if (args[0].equalsIgnoreCase("add") && args.length > 1) {
|
||||
final String line = FormatUtil.formatString(user, "essentials.itemlore", getFinalArg(args, 1)).trim();
|
||||
final List<String> lore = im.hasLore() ? im.getLore() : new ArrayList<>();
|
||||
lore.add(line);
|
||||
im.setLore(lore);
|
||||
item.setItemMeta(im);
|
||||
user.sendMessage(tl("itemloreSuccess", line));
|
||||
} else if (args[0].equalsIgnoreCase("clear")) {
|
||||
im.setLore(new ArrayList<>());
|
||||
item.setItemMeta(im);
|
||||
user.sendMessage(tl("itemloreClear"));
|
||||
} else if (args[0].equalsIgnoreCase("set") && args.length > 2 && NumberUtil.isInt(args[1])) {
|
||||
if (!im.hasLore()) {
|
||||
throw new Exception(tl("itemloreNoLore"));
|
||||
}
|
||||
|
||||
final int line = Integer.parseInt(args[1]);
|
||||
final String newLine = FormatUtil.formatString(user, "essentials.itemlore", getFinalArg(args, 2)).trim();
|
||||
final List<String> lore = im.getLore();
|
||||
try {
|
||||
lore.set(line - 1, newLine);
|
||||
} catch (final Exception e) {
|
||||
throw new Exception(tl("itemloreNoLine", line), e);
|
||||
}
|
||||
im.setLore(lore);
|
||||
item.setItemMeta(im);
|
||||
user.sendMessage(tl("itemloreSuccessLore", line, newLine));
|
||||
} else {
|
||||
throw new NotEnoughArgumentsException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) {
|
||||
if (args.length == 1) {
|
||||
return Lists.newArrayList("add", "set", "clear");
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.utils.FormatUtil;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Commanditemname extends EssentialsCommand {
|
||||
|
||||
public Commanditemname() {
|
||||
super("itemname");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
|
||||
final ItemStack item = user.getBase().getItemInHand();
|
||||
if (item.getType() == Material.AIR) {
|
||||
user.sendMessage(tl("itemnameInvalidItem", item.getType().toString().toLowerCase(Locale.ENGLISH).replace('_', ' ')));
|
||||
return;
|
||||
}
|
||||
|
||||
String name = FormatUtil.formatString(user, "essentials.itemname", getFinalArg(args, 0)).trim();
|
||||
if (name.isEmpty()) {
|
||||
name = null;
|
||||
}
|
||||
|
||||
final ItemMeta im = item.getItemMeta();
|
||||
im.setDisplayName(name);
|
||||
item.setItemMeta(im);
|
||||
user.sendMessage(name == null ? tl("itemnameClear") : tl("itemnameSuccess", name));
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.CommandSource;
|
||||
import com.earth2me.essentials.PlayerList;
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.utils.FormatUtil;
|
||||
import com.earth2me.essentials.utils.NumberUtil;
|
||||
import org.bukkit.Server;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Commandlist extends EssentialsCommand {
|
||||
public Commandlist() {
|
||||
super("list");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
boolean showHidden = true;
|
||||
User user = null;
|
||||
if (sender.isPlayer()) {
|
||||
user = ess.getUser(sender.getPlayer());
|
||||
showHidden = user.isAuthorized("essentials.list.hidden") || user.canInteractVanished();
|
||||
}
|
||||
sender.sendMessage(PlayerList.listSummary(ess, user, showHidden));
|
||||
final Map<String, List<User>> playerList = PlayerList.getPlayerLists(ess, user, showHidden);
|
||||
|
||||
if (args.length > 0) {
|
||||
sender.sendMessage(PlayerList.listGroupUsers(ess, playerList, args[0].toLowerCase()));
|
||||
} else {
|
||||
sendGroupedList(sender, commandLabel, playerList);
|
||||
}
|
||||
}
|
||||
|
||||
// Output the standard /list output, when no group is specified
|
||||
private void sendGroupedList(final CommandSource sender, final String commandLabel, final Map<String, List<User>> playerList) {
|
||||
final Set<String> configGroups = ess.getSettings().getListGroupConfig().keySet();
|
||||
final List<String> asterisk = new ArrayList<>();
|
||||
|
||||
// Loop through the custom defined groups and display them
|
||||
for (final String oConfigGroup : configGroups) {
|
||||
final String groupValue = ess.getSettings().getListGroupConfig().get(oConfigGroup).toString().trim();
|
||||
final String configGroup = oConfigGroup.toLowerCase();
|
||||
|
||||
// If the group value is an asterisk, then skip it, and handle it later
|
||||
if (groupValue.equals("*")) {
|
||||
asterisk.add(oConfigGroup);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the group value is hidden, we don't need to display it
|
||||
if (groupValue.equalsIgnoreCase("hidden")) {
|
||||
playerList.remove(configGroup);
|
||||
continue;
|
||||
}
|
||||
|
||||
final List<User> outputUserList;
|
||||
final List<User> matchedList = playerList.get(configGroup);
|
||||
|
||||
// If the group value is an int, then we might need to truncate it
|
||||
if (NumberUtil.isInt(groupValue)) {
|
||||
if (matchedList != null && !matchedList.isEmpty()) {
|
||||
playerList.remove(configGroup);
|
||||
outputUserList = new ArrayList<>(matchedList);
|
||||
final int limit = Integer.parseInt(groupValue);
|
||||
if (matchedList.size() > limit) {
|
||||
sender.sendMessage(PlayerList.outputFormat(oConfigGroup, tl("groupNumber", matchedList.size(), commandLabel, FormatUtil.stripFormat(configGroup))));
|
||||
} else {
|
||||
sender.sendMessage(PlayerList.outputFormat(oConfigGroup, PlayerList.listUsers(ess, outputUserList, ", ")));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
outputUserList = PlayerList.getMergedList(ess, playerList, configGroup);
|
||||
|
||||
// If we have no users, than we don't need to continue parsing this group
|
||||
if (outputUserList.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sender.sendMessage(PlayerList.outputFormat(oConfigGroup, PlayerList.listUsers(ess, outputUserList, ", ")));
|
||||
}
|
||||
|
||||
final Set<String> var = playerList.keySet();
|
||||
String[] onlineGroups = var.toArray(new String[0]);
|
||||
Arrays.sort(onlineGroups, String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
// If we have an asterisk group, then merge all remaining groups
|
||||
if (!asterisk.isEmpty()) {
|
||||
final List<User> asteriskUsers = new ArrayList<>();
|
||||
for (final String onlineGroup : onlineGroups) {
|
||||
asteriskUsers.addAll(playerList.get(onlineGroup));
|
||||
}
|
||||
for (final String key : asterisk) {
|
||||
playerList.put(key, asteriskUsers);
|
||||
}
|
||||
onlineGroups = asterisk.toArray(new String[0]);
|
||||
}
|
||||
|
||||
// If we have any groups remaining after the custom groups loop through and display them
|
||||
for (final String onlineGroup : onlineGroups) {
|
||||
final List<User> users = playerList.get(onlineGroup);
|
||||
String groupName = asterisk.isEmpty() ? users.get(0).getGroup() : onlineGroup;
|
||||
|
||||
if (ess.getPermissionsHandler().getName().equals("ConfigPermissions")) {
|
||||
groupName = tl("connectedPlayers");
|
||||
}
|
||||
if (users == null || users.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sender.sendMessage(PlayerList.outputFormat(groupName, PlayerList.listUsers(ess, users, ", ")));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
|
||||
if (args.length == 1) {
|
||||
return new ArrayList<>(PlayerList.getPlayerLists(ess, sender.getUser(ess), true).keySet());
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.CommandSource;
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.textreader.IText;
|
||||
import com.earth2me.essentials.textreader.SimpleTextInput;
|
||||
import com.earth2me.essentials.textreader.TextPager;
|
||||
import com.earth2me.essentials.utils.DateUtil;
|
||||
import com.earth2me.essentials.utils.FormatUtil;
|
||||
import com.earth2me.essentials.utils.StringUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.bukkit.Server;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Commandmail extends EssentialsCommand {
|
||||
private static int mailsPerMinute = 0;
|
||||
private static long timestamp = 0;
|
||||
|
||||
public Commandmail() {
|
||||
super("mail");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
|
||||
if (args.length >= 1 && "read".equalsIgnoreCase(args[0])) {
|
||||
final List<String> mail = user.getMails();
|
||||
if (mail.isEmpty()) {
|
||||
user.sendMessage(tl("noMail"));
|
||||
throw new NoChargeException();
|
||||
}
|
||||
|
||||
final IText input = new SimpleTextInput(mail);
|
||||
final TextPager pager = new TextPager(input);
|
||||
pager.showPage(args.length > 1 ? args[1] : null, null, commandLabel + " " + args[0], user.getSource());
|
||||
|
||||
user.sendMessage(tl("mailClear"));
|
||||
return;
|
||||
}
|
||||
if (args.length >= 3 && "send".equalsIgnoreCase(args[0])) {
|
||||
if (!user.isAuthorized("essentials.mail.send")) {
|
||||
throw new Exception(tl("noPerm", "essentials.mail.send"));
|
||||
}
|
||||
|
||||
if (user.isMuted()) {
|
||||
final String dateDiff = user.getMuteTimeout() > 0 ? DateUtil.formatDateDiff(user.getMuteTimeout()) : null;
|
||||
if (dateDiff == null) {
|
||||
throw new Exception(user.hasMuteReason() ? tl("voiceSilencedReason", user.getMuteReason()) : tl("voiceSilenced"));
|
||||
}
|
||||
throw new Exception(user.hasMuteReason() ? tl("voiceSilencedReasonTime", dateDiff, user.getMuteReason()) : tl("voiceSilencedTime", dateDiff));
|
||||
}
|
||||
|
||||
final User u;
|
||||
try {
|
||||
u = getPlayer(server, args[1], true, true);
|
||||
} catch (final PlayerNotFoundException e) {
|
||||
throw new Exception(tl("playerNeverOnServer", args[1]));
|
||||
}
|
||||
|
||||
final String mail = tl("mailFormat", user.getName(), FormatUtil.formatMessage(user, "essentials.mail", StringUtil.sanitizeString(FormatUtil.stripFormat(getFinalArg(args, 2)))));
|
||||
if (mail.length() > 1000) {
|
||||
throw new Exception(tl("mailTooLong"));
|
||||
}
|
||||
|
||||
if (!u.isIgnoredPlayer(user)) {
|
||||
if (Math.abs(System.currentTimeMillis() - timestamp) > 60000) {
|
||||
timestamp = System.currentTimeMillis();
|
||||
mailsPerMinute = 0;
|
||||
}
|
||||
mailsPerMinute++;
|
||||
if (mailsPerMinute > ess.getSettings().getMailsPerMinute()) {
|
||||
throw new Exception(tl("mailDelay", ess.getSettings().getMailsPerMinute()));
|
||||
}
|
||||
u.addMail(tl("mailMessage", mail));
|
||||
}
|
||||
|
||||
user.sendMessage(tl("mailSentTo", u.getDisplayName(), u.getName()));
|
||||
user.sendMessage(mail);
|
||||
return;
|
||||
}
|
||||
if (args.length > 1 && "sendall".equalsIgnoreCase(args[0])) {
|
||||
if (!user.isAuthorized("essentials.mail.sendall")) {
|
||||
throw new Exception(tl("noPerm", "essentials.mail.sendall"));
|
||||
}
|
||||
ess.runTaskAsynchronously(new SendAll(tl("mailFormat", user.getName(),
|
||||
FormatUtil.formatMessage(user, "essentials.mail", StringUtil.sanitizeString(FormatUtil.stripFormat(getFinalArg(args, 1)))))));
|
||||
user.sendMessage(tl("mailSent"));
|
||||
return;
|
||||
}
|
||||
if (args.length >= 1 && "clear".equalsIgnoreCase(args[0])) {
|
||||
if (user.getMails() == null || user.getMails().isEmpty()) {
|
||||
user.sendMessage(tl("noMail"));
|
||||
throw new NoChargeException();
|
||||
}
|
||||
|
||||
user.setMails(null);
|
||||
user.sendMessage(tl("mailCleared"));
|
||||
return;
|
||||
}
|
||||
throw new NotEnoughArgumentsException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
if (args.length >= 1 && "read".equalsIgnoreCase(args[0])) {
|
||||
throw new Exception(tl("onlyPlayers", commandLabel + " read"));
|
||||
} else if (args.length >= 1 && "clear".equalsIgnoreCase(args[0])) {
|
||||
throw new Exception(tl("onlyPlayers", commandLabel + " clear"));
|
||||
} else if (args.length >= 3 && "send".equalsIgnoreCase(args[0])) {
|
||||
final User u;
|
||||
try {
|
||||
u = getPlayer(server, args[1], true, true);
|
||||
} catch (final PlayerNotFoundException e) {
|
||||
throw new Exception(tl("playerNeverOnServer", args[1]));
|
||||
}
|
||||
u.addMail(tl("mailFormat", "Server", FormatUtil.replaceFormat(getFinalArg(args, 2))));
|
||||
sender.sendMessage(tl("mailSent"));
|
||||
return;
|
||||
} else if (args.length >= 2 && "sendall".equalsIgnoreCase(args[0])) {
|
||||
ess.runTaskAsynchronously(new SendAll(tl("mailFormat", "Server", FormatUtil.replaceFormat(getFinalArg(args, 1)))));
|
||||
sender.sendMessage(tl("mailSent"));
|
||||
return;
|
||||
} else if (args.length >= 2) {
|
||||
//allow sending from console without "send" argument, since it's the only thing the console can do
|
||||
final User u;
|
||||
try {
|
||||
u = getPlayer(server, args[0], true, true);
|
||||
} catch (final PlayerNotFoundException e) {
|
||||
throw new Exception(tl("playerNeverOnServer", args[0]));
|
||||
}
|
||||
u.addMail(tl("mailFormat", "Server", FormatUtil.replaceFormat(getFinalArg(args, 1))));
|
||||
sender.sendMessage(tl("mailSent"));
|
||||
return;
|
||||
}
|
||||
throw new NotEnoughArgumentsException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) {
|
||||
if (args.length == 1) {
|
||||
final List<String> options = Lists.newArrayList("read", "clear");
|
||||
if (user.isAuthorized("essentials.mail.send")) {
|
||||
options.add("send");
|
||||
}
|
||||
if (user.isAuthorized("essentials.mail.sendall")) {
|
||||
options.add("sendall");
|
||||
}
|
||||
return options;
|
||||
} else if (args.length == 2 && args[0].equalsIgnoreCase("send") && user.isAuthorized("essentials.mail.send")) {
|
||||
return getPlayers(server, user);
|
||||
} else if (args.length == 2 && args[0].equalsIgnoreCase("read")) {
|
||||
final List<String> mail = user.getMails();
|
||||
final int pages = mail.size() / 9 + (mail.size() % 9 > 0 ? 1 : 0);
|
||||
if (pages == 0) {
|
||||
return Lists.newArrayList("0");
|
||||
} else {
|
||||
final List<String> options = Lists.newArrayList("1");
|
||||
if (pages > 1) {
|
||||
options.add(String.valueOf(pages));
|
||||
}
|
||||
return options;
|
||||
}
|
||||
} else if ((args.length > 2 && args[0].equalsIgnoreCase("send") && user.isAuthorized("essentials.mail.send")) || (args.length > 1 && args[0].equalsIgnoreCase("sendall") && user.isAuthorized("essentials.mail.sendall"))) {
|
||||
return null; // Use vanilla handler
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
|
||||
if (args.length == 1) {
|
||||
return Lists.newArrayList("send", "sendall");
|
||||
} else if (args.length == 2 && args[0].equalsIgnoreCase("send")) {
|
||||
return getPlayers(server, sender);
|
||||
} else if ((args.length > 2 && args[0].equalsIgnoreCase("send")) || (args.length > 1 && args[0].equalsIgnoreCase("sendall"))) {
|
||||
return null; // Use vanilla handler
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
private class SendAll implements Runnable {
|
||||
final String message;
|
||||
|
||||
SendAll(final String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (final UUID userid : ess.getUserMap().getAllUniqueUsers()) {
|
||||
final User user = ess.getUserMap().getUser(userid);
|
||||
if (user != null) {
|
||||
user.addMail(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.CommandSource;
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.utils.EnumUtil;
|
||||
import com.earth2me.essentials.utils.NumberUtil;
|
||||
import com.earth2me.essentials.utils.VersionUtil;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.inventory.FurnaceRecipe;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.Recipe;
|
||||
import org.bukkit.inventory.ShapedRecipe;
|
||||
import org.bukkit.inventory.ShapelessRecipe;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Commandrecipe extends EssentialsCommand {
|
||||
|
||||
private static final Material FIREWORK_ROCKET = EnumUtil.getMaterial("FIREWORK_ROCKET", "FIREWORK");
|
||||
private static final Material FIREWORK_STAR = EnumUtil.getMaterial("FIREWORK_STAR", "FIREWORK_CHARGE");
|
||||
private static final Material GUNPOWDER = EnumUtil.getMaterial("GUNPOWDER", "SULPHUR");
|
||||
|
||||
public Commandrecipe() {
|
||||
super("recipe");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
// On versions at or above 1.12, we need recipe book API
|
||||
if (VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_12_0_R01)) {
|
||||
try {
|
||||
Class.forName("com.destroystokyo.paper.event.player.PlayerRecipeBookClickEvent");
|
||||
} catch (final ClassNotFoundException e) {
|
||||
sender.sendMessage(tl("unsupportedFeature"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (args.length < 1) {
|
||||
throw new NotEnoughArgumentsException();
|
||||
}
|
||||
|
||||
final ItemStack itemType = ess.getItemDb().get(args[0]);
|
||||
int recipeNo = 0;
|
||||
|
||||
if (args.length > 1) {
|
||||
if (NumberUtil.isInt(args[1])) {
|
||||
recipeNo = Integer.parseInt(args[1]) - 1;
|
||||
} else {
|
||||
throw new Exception(tl("invalidNumber"));
|
||||
}
|
||||
}
|
||||
|
||||
final List<Recipe> recipesOfType = ess.getServer().getRecipesFor(itemType);
|
||||
if (recipesOfType.size() < 1) {
|
||||
throw new Exception(tl("recipeNone", getMaterialName(itemType)));
|
||||
}
|
||||
|
||||
if (recipeNo < 0 || recipeNo >= recipesOfType.size()) {
|
||||
throw new Exception(tl("recipeBadIndex"));
|
||||
}
|
||||
|
||||
final Recipe selectedRecipe = recipesOfType.get(recipeNo);
|
||||
sender.sendMessage(tl("recipe", getMaterialName(itemType), recipeNo + 1, recipesOfType.size()));
|
||||
|
||||
if (selectedRecipe instanceof FurnaceRecipe) {
|
||||
furnaceRecipe(sender, (FurnaceRecipe) selectedRecipe);
|
||||
} else if (selectedRecipe instanceof ShapedRecipe) {
|
||||
shapedRecipe(sender, (ShapedRecipe) selectedRecipe, sender.isPlayer());
|
||||
} else if (selectedRecipe instanceof ShapelessRecipe) {
|
||||
if (recipesOfType.size() == 1 && (itemType.getType() == FIREWORK_ROCKET)) {
|
||||
final ShapelessRecipe shapelessRecipe = new ShapelessRecipe(itemType);
|
||||
shapelessRecipe.addIngredient(GUNPOWDER);
|
||||
shapelessRecipe.addIngredient(Material.PAPER);
|
||||
shapelessRecipe.addIngredient(FIREWORK_STAR);
|
||||
shapelessRecipe(sender, shapelessRecipe, sender.isPlayer());
|
||||
} else {
|
||||
shapelessRecipe(sender, (ShapelessRecipe) selectedRecipe, sender.isPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
if (recipesOfType.size() > 1 && args.length == 1) {
|
||||
sender.sendMessage(tl("recipeMore", commandLabel, args[0], getMaterialName(itemType)));
|
||||
}
|
||||
}
|
||||
|
||||
public void furnaceRecipe(final CommandSource sender, final FurnaceRecipe recipe) {
|
||||
sender.sendMessage(tl("recipeFurnace", getMaterialName(recipe.getInput())));
|
||||
}
|
||||
|
||||
public void shapedRecipe(final CommandSource sender, final ShapedRecipe recipe, final boolean showWindow) {
|
||||
final Map<Character, ItemStack> recipeMap = recipe.getIngredientMap();
|
||||
|
||||
if (showWindow) {
|
||||
final User user = ess.getUser(sender.getPlayer());
|
||||
user.getBase().closeInventory();
|
||||
user.setRecipeSee(true);
|
||||
final InventoryView view = user.getBase().openWorkbench(null, true);
|
||||
final String[] recipeShape = recipe.getShape();
|
||||
final Map<Character, ItemStack> ingredientMap = recipe.getIngredientMap();
|
||||
for (int j = 0; j < recipeShape.length; j++) {
|
||||
for (int k = 0; k < recipeShape[j].length(); k++) {
|
||||
final ItemStack item = ingredientMap.get(recipeShape[j].toCharArray()[k]);
|
||||
if (item == null) {
|
||||
continue;
|
||||
}
|
||||
if (item.getDurability() == Short.MAX_VALUE) {
|
||||
item.setDurability((short) 0);
|
||||
}
|
||||
view.getTopInventory().setItem(j * 3 + k + 1, item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final HashMap<Material, String> colorMap = new HashMap<>();
|
||||
int i = 1;
|
||||
for (final Character c : "abcdefghi".toCharArray()) {
|
||||
final ItemStack item = recipeMap.get(c);
|
||||
if (!colorMap.containsKey(item == null ? null : item.getType())) {
|
||||
colorMap.put(item == null ? null : item.getType(), String.valueOf(i++));
|
||||
}
|
||||
}
|
||||
final Material[][] materials = new Material[3][3];
|
||||
for (int j = 0; j < recipe.getShape().length; j++) {
|
||||
for (int k = 0; k < recipe.getShape()[j].length(); k++) {
|
||||
final ItemStack item = recipe.getIngredientMap().get(recipe.getShape()[j].toCharArray()[k]);
|
||||
materials[j][k] = item == null ? null : item.getType();
|
||||
}
|
||||
}
|
||||
sender.sendMessage(tl("recipeGrid", colorMap.get(materials[0][0]), colorMap.get(materials[0][1]), colorMap.get(materials[0][2])));
|
||||
sender.sendMessage(tl("recipeGrid", colorMap.get(materials[1][0]), colorMap.get(materials[1][1]), colorMap.get(materials[1][2])));
|
||||
sender.sendMessage(tl("recipeGrid", colorMap.get(materials[2][0]), colorMap.get(materials[2][1]), colorMap.get(materials[2][2])));
|
||||
|
||||
final StringBuilder s = new StringBuilder();
|
||||
for (final Material items : colorMap.keySet().toArray(new Material[0])) {
|
||||
s.append(tl("recipeGridItem", colorMap.get(items), getMaterialName(items)));
|
||||
}
|
||||
sender.sendMessage(tl("recipeWhere", s.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
public void shapelessRecipe(final CommandSource sender, final ShapelessRecipe recipe, final boolean showWindow) {
|
||||
final List<ItemStack> ingredients = recipe.getIngredientList();
|
||||
if (showWindow) {
|
||||
final User user = ess.getUser(sender.getPlayer());
|
||||
user.setRecipeSee(true);
|
||||
final InventoryView view = user.getBase().openWorkbench(null, true);
|
||||
for (int i = 0; i < ingredients.size(); i++) {
|
||||
final ItemStack item = ingredients.get(i);
|
||||
if (item.getDurability() == Short.MAX_VALUE) {
|
||||
item.setDurability((short) 0);
|
||||
}
|
||||
view.setItem(i + 1, item);
|
||||
}
|
||||
|
||||
} else {
|
||||
final StringBuilder s = new StringBuilder();
|
||||
for (int i = 0; i < ingredients.size(); i++) {
|
||||
s.append(getMaterialName(ingredients.get(i)));
|
||||
if (i != ingredients.size() - 1) {
|
||||
s.append(",");
|
||||
}
|
||||
s.append(" ");
|
||||
}
|
||||
sender.sendMessage(tl("recipeShapeless", s.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
public String getMaterialName(final ItemStack stack) {
|
||||
if (stack == null) {
|
||||
return tl("recipeNothing");
|
||||
}
|
||||
return getMaterialName(stack.getType());
|
||||
}
|
||||
|
||||
public String getMaterialName(final Material type) {
|
||||
if (type == null) {
|
||||
return tl("recipeNothing");
|
||||
}
|
||||
return type.toString().replace("_", " ").toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
|
||||
if (args.length == 1) {
|
||||
return getItems();
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.CommandSource;
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.UserMap;
|
||||
import com.earth2me.essentials.craftbukkit.BanLookup;
|
||||
import com.earth2me.essentials.utils.DateUtil;
|
||||
import com.earth2me.essentials.utils.FormatUtil;
|
||||
import com.earth2me.essentials.utils.StringUtil;
|
||||
import org.bukkit.BanEntry;
|
||||
import org.bukkit.BanList;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Server;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Commandseen extends EssentialsCommand {
|
||||
public Commandseen() {
|
||||
super("seen");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
final boolean showBan = sender.isAuthorized("essentials.seen.banreason", ess);
|
||||
final boolean showIp = sender.isAuthorized("essentials.seen.ip", ess);
|
||||
final boolean showLocation = sender.isAuthorized("essentials.seen.location", ess);
|
||||
if (args.length < 1) {
|
||||
throw new NotEnoughArgumentsException();
|
||||
}
|
||||
User player;
|
||||
// Check by uuid, if it fails check by name.
|
||||
try {
|
||||
final UUID uuid = UUID.fromString(args[0]);
|
||||
player = ess.getUser(uuid);
|
||||
} catch (final IllegalArgumentException ignored) { // Thrown if invalid UUID from string, check by name.
|
||||
player = ess.getOfflineUser(args[0]);
|
||||
}
|
||||
|
||||
if (player == null) {
|
||||
if (sender.isAuthorized("essentials.seen.ipsearch", ess) && FormatUtil.validIP(args[0])) {
|
||||
seenIP(sender, args[0]);
|
||||
return;
|
||||
} else if (ess.getServer().getBanList(BanList.Type.IP).isBanned(args[0])) {
|
||||
sender.sendMessage(tl("isIpBanned", args[0]));
|
||||
return;
|
||||
} else if (BanLookup.isBanned(ess, args[0])) {
|
||||
sender.sendMessage(tl("whoisBanned", showBan ? BanLookup.getBanEntry(ess, args[0]).getReason() : tl("true")));
|
||||
return;
|
||||
}
|
||||
ess.getScheduler().runTaskAsynchronously(ess, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final User userFromBukkit = ess.getUserMap().getUserFromBukkit(args[0]);
|
||||
try {
|
||||
if (userFromBukkit != null) {
|
||||
showUserSeen(userFromBukkit);
|
||||
} else {
|
||||
try {
|
||||
showUserSeen(getPlayer(server, sender, args, 0));
|
||||
} catch (final PlayerNotFoundException e) {
|
||||
throw new Exception(tl("playerNeverOnServer", args[0]));
|
||||
}
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
ess.showError(sender, e, commandLabel);
|
||||
}
|
||||
}
|
||||
|
||||
private void showUserSeen(final User user) throws Exception {
|
||||
showSeenMessage(sender, user, showBan, showIp, showLocation);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
showSeenMessage(sender, player, showBan, showIp, showLocation);
|
||||
}
|
||||
}
|
||||
|
||||
private void showSeenMessage(final CommandSource sender, final User player, final boolean showBan, final boolean showIp, final boolean showLocation) {
|
||||
if (player.getBase().isOnline() && canInteractWith(sender, player)) {
|
||||
seenOnline(sender, player, showIp);
|
||||
} else {
|
||||
seenOffline(sender, player, showBan, showIp, showLocation);
|
||||
}
|
||||
}
|
||||
|
||||
private void seenOnline(final CommandSource sender, final User user, final boolean showIp) {
|
||||
|
||||
user.setDisplayNick();
|
||||
sender.sendMessage(tl("seenOnline", user.getDisplayName(), DateUtil.formatDateDiff(user.getLastLogin())));
|
||||
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().info("UUID: " + user.getBase().getUniqueId().toString());
|
||||
}
|
||||
|
||||
final List<String> history = ess.getUserMap().getUserHistory(user.getBase().getUniqueId());
|
||||
if (history != null && history.size() > 1) {
|
||||
sender.sendMessage(tl("seenAccounts", StringUtil.joinListSkip(", ", user.getName(), history)));
|
||||
}
|
||||
|
||||
if (user.isAfk()) {
|
||||
sender.sendMessage(tl("whoisAFK", tl("true")));
|
||||
}
|
||||
if (user.isJailed()) {
|
||||
sender.sendMessage(tl("whoisJail", user.getJailTimeout() > 0 ? DateUtil.formatDateDiff(user.getJailTimeout()) : tl("true")));
|
||||
}
|
||||
if (user.isMuted()) {
|
||||
final long muteTimeout = user.getMuteTimeout();
|
||||
if (!user.hasMuteReason()) {
|
||||
sender.sendMessage(tl("whoisMuted", muteTimeout > 0 ? DateUtil.formatDateDiff(muteTimeout) : tl("true")));
|
||||
} else {
|
||||
sender.sendMessage(tl("whoisMutedReason", muteTimeout > 0 ? DateUtil.formatDateDiff(muteTimeout) : tl("true"), user.getMuteReason()));
|
||||
}
|
||||
}
|
||||
final String location = user.getGeoLocation();
|
||||
if (location != null && (!sender.isPlayer() || ess.getUser(sender.getPlayer()).isAuthorized("essentials.geoip.show"))) {
|
||||
sender.sendMessage(tl("whoisGeoLocation", location));
|
||||
}
|
||||
if (showIp) {
|
||||
sender.sendMessage(tl("whoisIPAddress", user.getBase().getAddress().getAddress().toString()));
|
||||
}
|
||||
}
|
||||
|
||||
private void seenOffline(final CommandSource sender, final User user, final boolean showBan, final boolean showIp, final boolean showLocation) {
|
||||
user.setDisplayNick();
|
||||
if (user.getLastLogout() > 0) {
|
||||
sender.sendMessage(tl("seenOffline", user.getName(), DateUtil.formatDateDiff(user.getLastLogout())));
|
||||
} else {
|
||||
sender.sendMessage(tl("userUnknown", user.getName()));
|
||||
}
|
||||
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().info("UUID: " + user.getBase().getUniqueId().toString());
|
||||
}
|
||||
|
||||
final List<String> history = ess.getUserMap().getUserHistory(user.getBase().getUniqueId());
|
||||
if (history != null && history.size() > 1) {
|
||||
sender.sendMessage(tl("seenAccounts", StringUtil.joinListSkip(", ", user.getName(), history)));
|
||||
}
|
||||
|
||||
if (BanLookup.isBanned(ess, user)) {
|
||||
final BanEntry banEntry = BanLookup.getBanEntry(ess, user.getName());
|
||||
final String reason = showBan ? banEntry.getReason() : tl("true");
|
||||
sender.sendMessage(tl("whoisBanned", reason));
|
||||
if (banEntry.getExpiration() != null) {
|
||||
final Date expiry = banEntry.getExpiration();
|
||||
String expireString = tl("now");
|
||||
if (expiry.after(new Date())) {
|
||||
expireString = DateUtil.formatDateDiff(expiry.getTime());
|
||||
}
|
||||
sender.sendMessage(tl("whoisTempBanned", expireString));
|
||||
}
|
||||
}
|
||||
|
||||
if (user.isMuted()) {
|
||||
final long muteTimeout = user.getMuteTimeout();
|
||||
if (!user.hasMuteReason()) {
|
||||
sender.sendMessage(tl("whoisMuted", muteTimeout > 0 ? DateUtil.formatDateDiff(muteTimeout) : tl("true")));
|
||||
} else {
|
||||
sender.sendMessage(tl("whoisMutedReason", muteTimeout > 0 ? DateUtil.formatDateDiff(muteTimeout) : tl("true"), user.getMuteReason()));
|
||||
}
|
||||
}
|
||||
|
||||
final String location = user.getGeoLocation();
|
||||
if (location != null && (!sender.isPlayer() || ess.getUser(sender.getPlayer()).isAuthorized("essentials.geoip.show"))) {
|
||||
sender.sendMessage(tl("whoisGeoLocation", location));
|
||||
}
|
||||
if (showIp) {
|
||||
if (!user.getLastLoginAddress().isEmpty()) {
|
||||
sender.sendMessage(tl("whoisIPAddress", user.getLastLoginAddress()));
|
||||
}
|
||||
}
|
||||
if (showLocation) {
|
||||
final Location loc = user.getLogoutLocation();
|
||||
if (loc != null) {
|
||||
sender.sendMessage(tl("whoisLocation", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void seenIP(final CommandSource sender, final String ipAddress) {
|
||||
final UserMap userMap = ess.getUserMap();
|
||||
|
||||
if (ess.getServer().getBanList(BanList.Type.IP).isBanned(ipAddress)) {
|
||||
sender.sendMessage(tl("isIpBanned", ipAddress));
|
||||
}
|
||||
|
||||
sender.sendMessage(tl("runningPlayerMatch", ipAddress));
|
||||
|
||||
ess.runTaskAsynchronously(() -> {
|
||||
final List<String> matches = new ArrayList<>();
|
||||
for (final UUID u : userMap.getAllUniqueUsers()) {
|
||||
final User user = ess.getUserMap().getUser(u);
|
||||
if (user == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final String uIPAddress = user.getLastLoginAddress();
|
||||
|
||||
if (!uIPAddress.isEmpty() && uIPAddress.equalsIgnoreCase(ipAddress)) {
|
||||
matches.add(user.getName());
|
||||
}
|
||||
}
|
||||
|
||||
if (matches.size() > 0) {
|
||||
sender.sendMessage(tl("matchingIPAddress"));
|
||||
sender.sendMessage(StringUtil.joinList(matches));
|
||||
} else {
|
||||
sender.sendMessage(tl("noMatchingPlayers"));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
|
||||
if (args.length == 1) {
|
||||
return getPlayers(server, sender);
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.RandomTeleport;
|
||||
import com.earth2me.essentials.User;
|
||||
import org.bukkit.Server;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Commandsettpr extends EssentialsCommand {
|
||||
public Commandsettpr() {
|
||||
super("settpr");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
|
||||
final RandomTeleport randomTeleport = ess.getRandomTeleport();
|
||||
randomTeleport.getCachedLocations().clear();
|
||||
if (args.length == 0 || "center".equalsIgnoreCase(args[0])) {
|
||||
randomTeleport.setCenter(user.getLocation());
|
||||
user.sendMessage(tl("settpr"));
|
||||
} else if (args.length > 1) {
|
||||
if ("minrange".equalsIgnoreCase(args[0])) {
|
||||
randomTeleport.setMinRange(Double.parseDouble(args[1]));
|
||||
} else if ("maxrange".equalsIgnoreCase(args[0])) {
|
||||
randomTeleport.setMaxRange(Double.parseDouble(args[1]));
|
||||
}
|
||||
user.sendMessage(tl("settprValue", args[0].toLowerCase(), args[1].toLowerCase()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) {
|
||||
if (args.length == 1) {
|
||||
return Arrays.asList("center", "minrange", "maxrange");
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.api.IWarps;
|
||||
import com.earth2me.essentials.utils.NumberUtil;
|
||||
import com.earth2me.essentials.utils.StringUtil;
|
||||
import net.ess3.api.InvalidWorldException;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Server;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Commandsetwarp extends EssentialsCommand {
|
||||
public Commandsetwarp() {
|
||||
super("setwarp");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
|
||||
if (args.length == 0) {
|
||||
throw new NotEnoughArgumentsException();
|
||||
}
|
||||
|
||||
if (NumberUtil.isInt(args[0]) || args[0].isEmpty()) {
|
||||
throw new Exception(tl("invalidWarpName"));
|
||||
}
|
||||
|
||||
final IWarps warps = ess.getWarps();
|
||||
Location warpLoc = null;
|
||||
|
||||
try {
|
||||
warpLoc = warps.getWarp(args[0]);
|
||||
} catch (final WarpNotFoundException | InvalidWorldException ignored) {
|
||||
}
|
||||
|
||||
if (warpLoc == null || user.isAuthorized("essentials.warp.overwrite." + StringUtil.safeString(args[0]))) {
|
||||
warps.setWarp(user, args[0], user.getLocation());
|
||||
} else {
|
||||
throw new Exception(tl("warpOverwrite"));
|
||||
}
|
||||
user.sendMessage(tl("warpSet", args[0]));
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.craftbukkit.InventoryWorkaround;
|
||||
import com.earth2me.essentials.utils.EnumUtil;
|
||||
import com.earth2me.essentials.utils.MaterialUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.SkullMeta;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Commandskull extends EssentialsCommand {
|
||||
|
||||
private static final Pattern NAME_PATTERN = Pattern.compile("^[A-Za-z0-9_]+$");
|
||||
private static final Material SKULL_ITEM = EnumUtil.getMaterial("PLAYER_HEAD", "SKULL_ITEM");
|
||||
|
||||
public Commandskull() {
|
||||
super("skull");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
|
||||
final String owner;
|
||||
if (args.length > 0 && user.isAuthorized("essentials.skull.others")) {
|
||||
if (!NAME_PATTERN.matcher(args[0]).matches()) {
|
||||
throw new IllegalArgumentException(tl("alphaNames"));
|
||||
}
|
||||
owner = args[0];
|
||||
} else {
|
||||
owner = user.getName();
|
||||
}
|
||||
|
||||
ItemStack itemSkull = user.getItemInHand();
|
||||
final SkullMeta metaSkull;
|
||||
boolean spawn = false;
|
||||
|
||||
if (itemSkull != null && MaterialUtil.isPlayerHead(itemSkull.getType(), itemSkull.getDurability())) {
|
||||
metaSkull = (SkullMeta) itemSkull.getItemMeta();
|
||||
} else if (user.isAuthorized("essentials.skull.spawn")) {
|
||||
itemSkull = new ItemStack(SKULL_ITEM, 1, (byte) 3);
|
||||
metaSkull = (SkullMeta) itemSkull.getItemMeta();
|
||||
spawn = true;
|
||||
} else {
|
||||
throw new Exception(tl("invalidSkull"));
|
||||
}
|
||||
|
||||
if (metaSkull.hasOwner() && !user.isAuthorized("essentials.skull.modify")) {
|
||||
throw new Exception(tl("noPermissionSkull"));
|
||||
}
|
||||
|
||||
editSkull(user, itemSkull, metaSkull, owner, spawn);
|
||||
}
|
||||
|
||||
private void editSkull(final User user, final ItemStack stack, final SkullMeta skullMeta, final String owner, final boolean spawn) {
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
//Run this stuff async because SkullMeta#setOwner causes a http request.
|
||||
skullMeta.setDisplayName("§fSkull of " + owner);
|
||||
skullMeta.setOwner(owner);
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
stack.setItemMeta(skullMeta);
|
||||
if (spawn) {
|
||||
InventoryWorkaround.addItems(user.getBase().getInventory(), stack);
|
||||
user.sendMessage(tl("givenSkull", owner));
|
||||
return;
|
||||
}
|
||||
user.sendMessage(tl("skullChanged", owner));
|
||||
}
|
||||
}.runTask(ess);
|
||||
}
|
||||
}.runTaskAsynchronously(ess);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) {
|
||||
if (args.length == 1) {
|
||||
if (user.isAuthorized("essentials.skull.others")) {
|
||||
return getPlayers(server, user);
|
||||
} else {
|
||||
return Lists.newArrayList(user.getName());
|
||||
}
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.CommandSource;
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.utils.DateUtil;
|
||||
import net.ess3.api.events.JailStatusChangeEvent;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Commandtogglejail extends EssentialsCommand {
|
||||
public Commandtogglejail() {
|
||||
super("togglejail");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
if (args.length == 0) {
|
||||
throw new NotEnoughArgumentsException();
|
||||
}
|
||||
|
||||
final User player = getPlayer(server, args, 0, true, true);
|
||||
|
||||
if (args.length >= 2 && !player.isJailed()) {
|
||||
if (!player.getBase().isOnline()) {
|
||||
if (sender.isPlayer() && !ess.getUser(sender.getPlayer()).isAuthorized("essentials.togglejail.offline")) {
|
||||
sender.sendMessage(tl("mayNotJailOffline"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (player.isAuthorized("essentials.jail.exempt")) {
|
||||
sender.sendMessage(tl("mayNotJail"));
|
||||
return;
|
||||
}
|
||||
|
||||
final JailStatusChangeEvent event = new JailStatusChangeEvent(player, sender.isPlayer() ? ess.getUser(sender.getPlayer()) : null, true);
|
||||
ess.getServer().getPluginManager().callEvent(event);
|
||||
|
||||
if (!event.isCancelled()) {
|
||||
long preTimeDiff = 0;
|
||||
if (args.length > 2) {
|
||||
final String time = getFinalArg(args, 2);
|
||||
preTimeDiff = DateUtil.parseDateDiff(time, true);
|
||||
|
||||
}
|
||||
final long timeDiff = preTimeDiff;
|
||||
final CompletableFuture<Boolean> future = getNewExceptionFuture(sender, commandLabel);
|
||||
future.thenAccept(success -> {
|
||||
if (success) {
|
||||
player.setJailed(true);
|
||||
player.sendMessage(tl("userJailed"));
|
||||
player.setJail(null);
|
||||
player.setJail(args[1]);
|
||||
if (args.length > 2) {
|
||||
player.setJailTimeout(timeDiff);
|
||||
}
|
||||
sender.sendMessage(timeDiff > 0 ? tl("playerJailedFor", player.getName(), DateUtil.formatDateDiff(timeDiff)) : tl("playerJailed", player.getName()));
|
||||
}
|
||||
});
|
||||
if (player.getBase().isOnline()) {
|
||||
ess.getJails().sendToJail(player, args[1], future);
|
||||
} else {
|
||||
// Check if jail exists
|
||||
ess.getJails().getJail(args[1]);
|
||||
future.complete(true);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.length >= 2 && player.isJailed() && !args[1].equalsIgnoreCase(player.getJail())) {
|
||||
sender.sendMessage(tl("jailAlreadyIncarcerated", player.getJail()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.length >= 2 && player.isJailed() && args[1].equalsIgnoreCase(player.getJail())) {
|
||||
final long timeDiff = DateUtil.parseDateDiff(getFinalArg(args, 2), true);
|
||||
player.setJailTimeout(timeDiff);
|
||||
sender.sendMessage(tl("jailSentenceExtended", DateUtil.formatDateDiff(timeDiff)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.length == 1 || (args.length == 2 && args[1].equalsIgnoreCase(player.getJail()))) {
|
||||
if (!player.isJailed()) {
|
||||
throw new NotEnoughArgumentsException();
|
||||
}
|
||||
|
||||
final JailStatusChangeEvent event = new JailStatusChangeEvent(player, sender.isPlayer() ? ess.getUser(sender.getPlayer()) : null, false);
|
||||
ess.getServer().getPluginManager().callEvent(event);
|
||||
|
||||
if (!event.isCancelled()) {
|
||||
player.setJailed(false);
|
||||
player.setJailTimeout(0);
|
||||
player.sendMessage(tl("jailReleasedPlayerNotify"));
|
||||
player.setJail(null);
|
||||
if (player.getBase().isOnline() && ess.getSettings().isTeleportBackWhenFreedFromJail()) {
|
||||
final CompletableFuture<Boolean> future = getNewExceptionFuture(sender, commandLabel);
|
||||
player.getAsyncTeleport().back(future);
|
||||
future.thenAccept(success -> {
|
||||
if (success) {
|
||||
sender.sendMessage(tl("jailReleased", player.getName()));
|
||||
}
|
||||
});
|
||||
future.exceptionally(e -> {
|
||||
player.getAsyncTeleport().respawn(null, PlayerTeleportEvent.TeleportCause.PLUGIN, new CompletableFuture<>());
|
||||
sender.sendMessage(tl("jailReleased", player.getName()));
|
||||
return false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(tl("jailReleased", player.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
|
||||
if (args.length == 1) {
|
||||
return getPlayers(server, sender);
|
||||
} else if (args.length == 2) {
|
||||
try {
|
||||
return new ArrayList<>(ess.getJails().getList());
|
||||
} catch (final Exception e) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
} else {
|
||||
return COMMON_DATE_DIFFS;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.AsyncTeleport;
|
||||
import com.earth2me.essentials.Trade;
|
||||
import com.earth2me.essentials.User;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Commandtpaccept extends EssentialsCommand {
|
||||
public Commandtpaccept() {
|
||||
super("tpaccept");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
|
||||
final User requester;
|
||||
try {
|
||||
requester = ess.getUser(user.getTeleportRequest());
|
||||
} catch (final Exception ex) {
|
||||
throw new Exception(tl("noPendingRequest"));
|
||||
}
|
||||
|
||||
if (!requester.getBase().isOnline()) {
|
||||
throw new Exception(tl("noPendingRequest"));
|
||||
}
|
||||
|
||||
if (user.isTpRequestHere() && ((!requester.isAuthorized("essentials.tpahere") && !requester.isAuthorized("essentials.tpaall")) || (user.getWorld() != requester.getWorld() && ess.getSettings().isWorldTeleportPermissions() && !user.isAuthorized("essentials.worlds." + user.getWorld().getName())))) {
|
||||
throw new Exception(tl("noPendingRequest"));
|
||||
}
|
||||
|
||||
if (!user.isTpRequestHere() && (!requester.isAuthorized("essentials.tpa") || (user.getWorld() != requester.getWorld() && ess.getSettings().isWorldTeleportPermissions() && !user.isAuthorized("essentials.worlds." + requester.getWorld().getName())))) {
|
||||
throw new Exception(tl("noPendingRequest"));
|
||||
}
|
||||
|
||||
if (args.length > 0 && !requester.getName().contains(args[0])) {
|
||||
throw new Exception(tl("noPendingRequest"));
|
||||
}
|
||||
|
||||
if (!user.hasOutstandingTeleportRequest()) {
|
||||
user.requestTeleport(null, false);
|
||||
throw new Exception(tl("requestTimedOut"));
|
||||
}
|
||||
|
||||
final Trade charge = new Trade(this.getName(), ess);
|
||||
user.sendMessage(tl("requestAccepted"));
|
||||
requester.sendMessage(tl("requestAcceptedFrom", user.getDisplayName()));
|
||||
|
||||
final CompletableFuture<Boolean> future = getNewExceptionFuture(requester.getSource(), commandLabel);
|
||||
future.exceptionally(e -> {
|
||||
user.sendMessage(tl("pendingTeleportCancelled"));
|
||||
return false;
|
||||
});
|
||||
if (user.isTpRequestHere()) {
|
||||
final Location loc = user.getTpRequestLocation();
|
||||
final AsyncTeleport teleport = requester.getAsyncTeleport();
|
||||
teleport.setTpType(AsyncTeleport.TeleportType.TPA);
|
||||
future.thenAccept(success -> {
|
||||
if (success) {
|
||||
requester.sendMessage(tl("teleporting", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
|
||||
}
|
||||
});
|
||||
teleport.teleportPlayer(user, user.getTpRequestLocation(), charge, TeleportCause.COMMAND, future);
|
||||
} else {
|
||||
final AsyncTeleport teleport = requester.getAsyncTeleport();
|
||||
teleport.setTpType(AsyncTeleport.TeleportType.TPA);
|
||||
teleport.teleport(user.getBase(), charge, TeleportCause.COMMAND, future);
|
||||
}
|
||||
user.requestTeleport(null, false);
|
||||
throw new NoChargeException();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.User;
|
||||
import org.bukkit.Server;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Commandtpdeny extends EssentialsCommand {
|
||||
public Commandtpdeny() {
|
||||
super("tpdeny");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
|
||||
if (user.getTeleportRequest() == null) {
|
||||
throw new Exception(tl("noPendingRequest"));
|
||||
}
|
||||
final User player = ess.getUser(user.getTeleportRequest());
|
||||
if (player == null) {
|
||||
throw new Exception(tl("noPendingRequest"));
|
||||
}
|
||||
|
||||
user.sendMessage(tl("requestDenied"));
|
||||
player.sendMessage(tl("requestDeniedFrom", user.getDisplayName()));
|
||||
user.requestTeleport(null, false);
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.RandomTeleport;
|
||||
import com.earth2me.essentials.Trade;
|
||||
import com.earth2me.essentials.User;
|
||||
import net.ess3.api.events.UserRandomTeleportEvent;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Commandtpr extends EssentialsCommand {
|
||||
|
||||
public Commandtpr() {
|
||||
super("tpr");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception {
|
||||
final Trade charge = new Trade(this.getName(), ess);
|
||||
charge.isAffordableFor(user);
|
||||
final RandomTeleport randomTeleport = ess.getRandomTeleport();
|
||||
final UserRandomTeleportEvent event = new UserRandomTeleportEvent(user, randomTeleport.getCenter(), randomTeleport.getMinRange(), randomTeleport.getMaxRange());
|
||||
server.getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
randomTeleport.getRandomLocation(event.getCenter(), event.getMinRange(), event.getMaxRange()).thenAccept(location -> {
|
||||
final CompletableFuture<Boolean> future = getNewExceptionFuture(user.getSource(), commandLabel);
|
||||
user.getAsyncTeleport().teleport(location, charge, PlayerTeleportEvent.TeleportCause.COMMAND, future);
|
||||
future.thenAccept(success -> {
|
||||
if (success) {
|
||||
user.sendMessage(tl("tprSuccess"));
|
||||
}
|
||||
});
|
||||
});
|
||||
throw new NoChargeException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getTabCompleteOptions(final Server server, final User user, final String commandLabel, final String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.CommandSource;
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.craftbukkit.SetExpFix;
|
||||
import com.earth2me.essentials.utils.DateUtil;
|
||||
import com.earth2me.essentials.utils.EnumUtil;
|
||||
import com.earth2me.essentials.utils.NumberUtil;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.Statistic;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Commandwhois extends EssentialsCommand {
|
||||
// For some reason, in 1.13 PLAY_ONE_MINUTE = ticks played = what used to be PLAY_ONE_TICK
|
||||
// https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/commits/b848d8ce633871b52115247b089029749c02f579
|
||||
private static final Statistic PLAY_ONE_TICK = EnumUtil.getStatistic("PLAY_ONE_MINUTE", "PLAY_ONE_TICK");
|
||||
|
||||
public Commandwhois() {
|
||||
super("whois");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
if (args.length < 1) {
|
||||
throw new NotEnoughArgumentsException();
|
||||
}
|
||||
|
||||
final User user = getPlayer(server, sender, args, 0);
|
||||
|
||||
sender.sendMessage(tl("whoisTop", user.getName()));
|
||||
user.setDisplayNick();
|
||||
sender.sendMessage(tl("whoisNick", user.getDisplayName()));
|
||||
sender.sendMessage(tl("whoisUuid", user.getBase().getUniqueId().toString()));
|
||||
sender.sendMessage(tl("whoisHealth", user.getBase().getHealth()));
|
||||
sender.sendMessage(tl("whoisHunger", user.getBase().getFoodLevel(), user.getBase().getSaturation()));
|
||||
sender.sendMessage(tl("whoisExp", SetExpFix.getTotalExperience(user.getBase()), user.getBase().getLevel()));
|
||||
sender.sendMessage(tl("whoisLocation", user.getLocation().getWorld().getName(), user.getLocation().getBlockX(), user.getLocation().getBlockY(), user.getLocation().getBlockZ()));
|
||||
final long playtimeMs = System.currentTimeMillis() - (user.getBase().getStatistic(PLAY_ONE_TICK) * 50);
|
||||
sender.sendMessage(tl("whoisPlaytime", DateUtil.formatDateDiff(playtimeMs)));
|
||||
if (!ess.getSettings().isEcoDisabled()) {
|
||||
sender.sendMessage(tl("whoisMoney", NumberUtil.displayCurrency(user.getMoney(), ess)));
|
||||
}
|
||||
if (!sender.isPlayer() || ess.getUser(sender.getPlayer()).isAuthorized("essentials.whois.ip")) {
|
||||
sender.sendMessage(tl("whoisIPAddress", user.getBase().getAddress().getAddress().toString()));
|
||||
}
|
||||
final String location = user.getGeoLocation();
|
||||
if (location != null && (!sender.isPlayer() || ess.getUser(sender.getPlayer()).isAuthorized("essentials.geoip.show"))) {
|
||||
sender.sendMessage(tl("whoisGeoLocation", location));
|
||||
}
|
||||
sender.sendMessage(tl("whoisGamemode", tl(user.getBase().getGameMode().toString().toLowerCase(Locale.ENGLISH))));
|
||||
sender.sendMessage(tl("whoisGod", user.isGodModeEnabled() ? tl("true") : tl("false")));
|
||||
sender.sendMessage(tl("whoisOp", user.getBase().isOp() ? tl("true") : tl("false")));
|
||||
sender.sendMessage(tl("whoisFly", user.getBase().getAllowFlight() ? tl("true") : tl("false"), user.getBase().isFlying() ? tl("flying") : tl("notFlying")));
|
||||
sender.sendMessage(tl("whoisSpeed", user.getBase().isFlying() ? user.getBase().getFlySpeed() : user.getBase().getWalkSpeed()));
|
||||
if (user.isAfk()) {
|
||||
sender.sendMessage(tl("whoisAFKSince", tl("true"), DateUtil.formatDateDiff(user.getAfkSince())));
|
||||
} else {
|
||||
sender.sendMessage(tl("whoisAFK", tl("false")));
|
||||
}
|
||||
sender.sendMessage(tl("whoisJail", user.isJailed() ? user.getJailTimeout() > 0 ? DateUtil.formatDateDiff(user.getJailTimeout()) : tl("true") : tl("false")));
|
||||
|
||||
final long muteTimeout = user.getMuteTimeout();
|
||||
if (!user.hasMuteReason()) {
|
||||
sender.sendMessage(tl("whoisMuted", user.isMuted() ? muteTimeout > 0 ? DateUtil.formatDateDiff(muteTimeout) : tl("true") : tl("false")));
|
||||
} else {
|
||||
sender.sendMessage(tl("whoisMutedReason", user.isMuted() ? muteTimeout > 0 ? DateUtil.formatDateDiff(muteTimeout) : tl("true") : tl("false"),
|
||||
user.getMuteReason()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getTabCompleteOptions(final Server server, final CommandSource sender, final String commandLabel, final String[] args) {
|
||||
if (args.length == 1) {
|
||||
return getPlayers(server, sender);
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
public class PlayerExemptException extends NoSuchFieldException {
|
||||
public PlayerExemptException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class PlayerNotFoundException extends NoSuchFieldException {
|
||||
public PlayerNotFoundException() {
|
||||
super(tl("playerNotFound"));
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class WarpNotFoundException extends Exception {
|
||||
public WarpNotFoundException() {
|
||||
super(tl("warpNotExist"));
|
||||
}
|
||||
|
||||
public WarpNotFoundException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,288 +0,0 @@
|
||||
package com.earth2me.essentials.craftbukkit;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.EntityEquipment;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
* This class can be removed when https://github.com/Bukkit/CraftBukkit/pull/193 is accepted to CraftBukkit
|
||||
*/
|
||||
|
||||
public final class InventoryWorkaround {
|
||||
/*
|
||||
Spigot 1.9, for whatever reason, decided to merge the armor and main player inventories without providing a way
|
||||
to access the main inventory. There's lots of ugly code in here to work around that.
|
||||
*/
|
||||
private static final int USABLE_PLAYER_INV_SIZE = 36;
|
||||
// Hot-ish code so cache
|
||||
private static Boolean hasMainHandSupport = null;
|
||||
|
||||
private InventoryWorkaround() {
|
||||
}
|
||||
|
||||
private static int firstPartial(final Inventory inventory, final ItemStack item, final int maxAmount) {
|
||||
if (item == null) {
|
||||
return -1;
|
||||
}
|
||||
final ItemStack[] stacks = inventory.getContents();
|
||||
for (int i = 0; i < stacks.length; i++) {
|
||||
final ItemStack cItem = stacks[i];
|
||||
if (cItem != null && cItem.getAmount() < maxAmount && cItem.isSimilar(item)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static boolean isCombinedInventory(final Inventory inventory) {
|
||||
return inventory instanceof PlayerInventory && inventory.getContents().length > USABLE_PLAYER_INV_SIZE;
|
||||
}
|
||||
|
||||
// Clears inventory without clearing armor
|
||||
public static void clearInventoryNoArmor(final PlayerInventory inventory) {
|
||||
if (isCombinedInventory(inventory)) {
|
||||
for (int i = 0; i < USABLE_PLAYER_INV_SIZE; i++) {
|
||||
inventory.setItem(i, null);
|
||||
}
|
||||
} else {
|
||||
inventory.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static Inventory makeTruncatedPlayerInventory(final PlayerInventory playerInventory) {
|
||||
final Inventory fakeInventory = Bukkit.getServer().createInventory(null, USABLE_PLAYER_INV_SIZE);
|
||||
fakeInventory.setContents(Arrays.copyOf(playerInventory.getContents(), fakeInventory.getSize()));
|
||||
return fakeInventory;
|
||||
}
|
||||
|
||||
// Returns what it couldn't store
|
||||
// This will will abort if it couldn't store all items
|
||||
public static Map<Integer, ItemStack> addAllItems(final Inventory inventory, final ItemStack... items) {
|
||||
final ItemStack[] contents = inventory.getContents();
|
||||
|
||||
final Inventory fakeInventory;
|
||||
if (isCombinedInventory(inventory)) {
|
||||
fakeInventory = makeTruncatedPlayerInventory((PlayerInventory) inventory);
|
||||
} else {
|
||||
fakeInventory = Bukkit.getServer().createInventory(null, inventory.getType());
|
||||
fakeInventory.setContents(contents);
|
||||
}
|
||||
final Map<Integer, ItemStack> overflow = addItems(fakeInventory, items);
|
||||
if (overflow.isEmpty()) {
|
||||
addItems(inventory, items);
|
||||
return null;
|
||||
}
|
||||
return addItems(fakeInventory, items);
|
||||
}
|
||||
|
||||
public static Map<Integer, ItemStack> addAllOversizedItems(final Inventory inventory, final int oversizedStacks, final ItemStack... items) {
|
||||
final ItemStack[] contents = inventory.getContents();
|
||||
|
||||
final Inventory fakeInventory;
|
||||
if (isCombinedInventory(inventory)) {
|
||||
fakeInventory = makeTruncatedPlayerInventory((PlayerInventory) inventory);
|
||||
} else {
|
||||
fakeInventory = Bukkit.getServer().createInventory(null, inventory.getType());
|
||||
fakeInventory.setContents(contents);
|
||||
}
|
||||
final Map<Integer, ItemStack> overflow = addOversizedItems(fakeInventory, oversizedStacks, items);
|
||||
if (overflow.isEmpty()) {
|
||||
addOversizedItems(inventory, oversizedStacks, items);
|
||||
return null;
|
||||
}
|
||||
return overflow;
|
||||
}
|
||||
|
||||
// Returns what it couldn't store
|
||||
public static Map<Integer, ItemStack> addItems(final Inventory inventory, final ItemStack... items) {
|
||||
return addOversizedItems(inventory, 0, items);
|
||||
}
|
||||
|
||||
// Returns what it couldn't store
|
||||
// Set oversizedStack to below normal stack size to disable oversized stacks
|
||||
public static Map<Integer, ItemStack> addOversizedItems(final Inventory inventory, final int oversizedStacks, final ItemStack... items) {
|
||||
if (isCombinedInventory(inventory)) {
|
||||
final Inventory fakeInventory = makeTruncatedPlayerInventory((PlayerInventory) inventory);
|
||||
final Map<Integer, ItemStack> overflow = addOversizedItems(fakeInventory, oversizedStacks, items);
|
||||
for (int i = 0; i < fakeInventory.getContents().length; i++) {
|
||||
inventory.setItem(i, fakeInventory.getContents()[i]);
|
||||
}
|
||||
return overflow;
|
||||
}
|
||||
|
||||
final Map<Integer, ItemStack> leftover = new HashMap<>();
|
||||
|
||||
/*
|
||||
* TODO: some optimization - Create a 'firstPartial' with a 'fromIndex' - Record the lastPartial per Material -
|
||||
* Cache firstEmpty result
|
||||
*/
|
||||
|
||||
// combine items
|
||||
|
||||
final ItemStack[] combined = new ItemStack[items.length];
|
||||
for (final ItemStack item : items) {
|
||||
if (item == null || item.getAmount() < 1) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < combined.length; j++) {
|
||||
if (combined[j] == null) {
|
||||
combined[j] = item.clone();
|
||||
break;
|
||||
}
|
||||
if (combined[j].isSimilar(item)) {
|
||||
combined[j].setAmount(combined[j].getAmount() + item.getAmount());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < combined.length; i++) {
|
||||
final ItemStack item = combined[i];
|
||||
if (item == null || item.getType() == Material.AIR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
// Do we already have a stack of it?
|
||||
final int maxAmount = Math.max(oversizedStacks, item.getType().getMaxStackSize());
|
||||
final int firstPartial = firstPartial(inventory, item, maxAmount);
|
||||
|
||||
// Drat! no partial stack
|
||||
if (firstPartial == -1) {
|
||||
// Find a free spot!
|
||||
final int firstFree = inventory.firstEmpty();
|
||||
|
||||
if (firstFree == -1) {
|
||||
// No space at all!
|
||||
leftover.put(i, item);
|
||||
break;
|
||||
} else {
|
||||
// More than a single stack!
|
||||
if (item.getAmount() > maxAmount) {
|
||||
final ItemStack stack = item.clone();
|
||||
stack.setAmount(maxAmount);
|
||||
inventory.setItem(firstFree, stack);
|
||||
item.setAmount(item.getAmount() - maxAmount);
|
||||
} else {
|
||||
// Just store it
|
||||
inventory.setItem(firstFree, item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// So, apparently it might only partially fit, well lets do just that
|
||||
final ItemStack partialItem = inventory.getItem(firstPartial);
|
||||
|
||||
final int amount = item.getAmount();
|
||||
final int partialAmount = partialItem.getAmount();
|
||||
|
||||
// Check if it fully fits
|
||||
if (amount + partialAmount <= maxAmount) {
|
||||
partialItem.setAmount(amount + partialAmount);
|
||||
break;
|
||||
}
|
||||
|
||||
// It fits partially
|
||||
partialItem.setAmount(maxAmount);
|
||||
item.setAmount(amount + partialAmount - maxAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
return leftover;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void setItemInMainHand(final Player p, final ItemStack item) {
|
||||
if (hasMainHandSupport == null) {
|
||||
try {
|
||||
p.getInventory().setItemInMainHand(item);
|
||||
hasMainHandSupport = true;
|
||||
} catch (final Throwable e) {
|
||||
p.setItemInHand(item);
|
||||
hasMainHandSupport = false;
|
||||
}
|
||||
} else {
|
||||
if (hasMainHandSupport) {
|
||||
p.getInventory().setItemInMainHand(item);
|
||||
} else {
|
||||
p.setItemInHand(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void setItemInMainHand(final EntityEquipment invent, final ItemStack item) {
|
||||
if (hasMainHandSupport == null) {
|
||||
try {
|
||||
invent.setItemInMainHand(item);
|
||||
hasMainHandSupport = true;
|
||||
} catch (final Throwable e) {
|
||||
invent.setItemInHand(item);
|
||||
hasMainHandSupport = false;
|
||||
}
|
||||
} else {
|
||||
if (hasMainHandSupport) {
|
||||
invent.setItemInMainHand(item);
|
||||
} else {
|
||||
invent.setItemInHand(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void setItemInMainHandDropChance(final EntityEquipment invent, final float chance) {
|
||||
if (hasMainHandSupport == null) {
|
||||
try {
|
||||
invent.setItemInMainHandDropChance(chance);
|
||||
hasMainHandSupport = true;
|
||||
} catch (final Throwable e) {
|
||||
invent.setItemInHandDropChance(chance);
|
||||
hasMainHandSupport = false;
|
||||
}
|
||||
} else {
|
||||
if (hasMainHandSupport) {
|
||||
invent.setItemInMainHandDropChance(chance);
|
||||
} else {
|
||||
invent.setItemInHandDropChance(chance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void setItemInOffHand(final Player p, final ItemStack item) {
|
||||
// This assumes that all builds that support a main hand also support an off hand.
|
||||
if (hasMainHandSupport == null || hasMainHandSupport) {
|
||||
try {
|
||||
p.getInventory().setItemInOffHand(item);
|
||||
hasMainHandSupport = true;
|
||||
} catch (final Throwable e) {
|
||||
hasMainHandSupport = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int clearItemInOffHand(final Player p, final ItemStack item) {
|
||||
// This should be added because if `/clear` itself is not initilized it will return an Error: null.
|
||||
if (hasMainHandSupport == null || hasMainHandSupport) {
|
||||
try {
|
||||
int removedAmount = 0;
|
||||
if (p.getInventory().getItemInOffHand().getType().equals(item.getType())) {
|
||||
removedAmount = p.getInventory().getItemInOffHand().getAmount();
|
||||
p.getInventory().setItemInOffHand(null);
|
||||
}
|
||||
hasMainHandSupport = true;
|
||||
return removedAmount;
|
||||
} catch (final Throwable e) {
|
||||
hasMainHandSupport = false;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,729 +0,0 @@
|
||||
package com.earth2me.essentials.metrics;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.logging.Level;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
/**
|
||||
* bStats collects some data for plugin authors.
|
||||
* <p>
|
||||
* Check out https://bStats.org/ to learn more about bStats!
|
||||
*/
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public class Metrics {
|
||||
|
||||
// The version of this bStats class
|
||||
public static final int B_STATS_VERSION = 1;
|
||||
// The url to which the data is sent
|
||||
private static final String URL = "https://bStats.org/submitData/bukkit";
|
||||
// Should failed requests be logged?
|
||||
private static boolean logFailedRequests;
|
||||
// Should the sent data be logged?
|
||||
private static boolean logSentData;
|
||||
// Should the response text be logged?
|
||||
private static boolean logResponseStatusText;
|
||||
// The uuid of the server
|
||||
private static String serverUUID;
|
||||
|
||||
static {
|
||||
// You can use the property to disable the check in your test environment
|
||||
if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) {
|
||||
// Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D
|
||||
final String defaultPackage = new String(
|
||||
new byte[] {'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'});
|
||||
final String examplePackage = new String(new byte[] {'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
|
||||
// We want to make sure nobody just copy & pastes the example and use the wrong package names
|
||||
if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) {
|
||||
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is bStats enabled on this server?
|
||||
private final boolean enabled;
|
||||
// The plugin
|
||||
private final Plugin plugin;
|
||||
|
||||
// The plugin id
|
||||
private final int pluginId;
|
||||
|
||||
// A list with all custom charts
|
||||
private final List<CustomChart> charts = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param plugin The plugin which stats should be submitted.
|
||||
* @param pluginId The id of the plugin.
|
||||
* It can be found at <a href="https://bstats.org/what-is-my-plugin-id">What is my plugin id?</a>
|
||||
*/
|
||||
public Metrics(final Plugin plugin, final int pluginId) {
|
||||
if (plugin == null) {
|
||||
throw new IllegalArgumentException("Plugin cannot be null!");
|
||||
}
|
||||
this.plugin = plugin;
|
||||
this.pluginId = pluginId;
|
||||
|
||||
// Get the config file
|
||||
final File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
|
||||
final File configFile = new File(bStatsFolder, "config.yml");
|
||||
final YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
||||
|
||||
// Check if the config file exists
|
||||
if (!config.isSet("serverUuid")) {
|
||||
|
||||
// Add default values
|
||||
config.addDefault("enabled", true);
|
||||
// Every server gets it's unique random id.
|
||||
config.addDefault("serverUuid", UUID.randomUUID().toString());
|
||||
// Should failed request be logged?
|
||||
config.addDefault("logFailedRequests", false);
|
||||
// Should the sent data be logged?
|
||||
config.addDefault("logSentData", false);
|
||||
// Should the response text be logged?
|
||||
config.addDefault("logResponseStatusText", false);
|
||||
|
||||
// Inform the server owners about bStats
|
||||
config.options().header(
|
||||
"bStats collects some data for plugin authors like how many servers are using their plugins.\n" +
|
||||
"To honor their work, you should not disable it.\n" +
|
||||
"This has nearly no effect on the server performance!\n" +
|
||||
"Check out https://bStats.org/ to learn more :)"
|
||||
).copyDefaults(true);
|
||||
try {
|
||||
config.save(configFile);
|
||||
} catch (final IOException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
// Load the data
|
||||
enabled = config.getBoolean("enabled", true);
|
||||
serverUUID = config.getString("serverUuid");
|
||||
logFailedRequests = config.getBoolean("logFailedRequests", false);
|
||||
logSentData = config.getBoolean("logSentData", false);
|
||||
logResponseStatusText = config.getBoolean("logResponseStatusText", false);
|
||||
|
||||
if (enabled) {
|
||||
boolean found = false;
|
||||
// Search for all other bStats Metrics classes to see if we are the first one
|
||||
for (final Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
|
||||
try {
|
||||
service.getField("B_STATS_VERSION"); // Our identifier :)
|
||||
found = true; // We aren't the first
|
||||
break;
|
||||
} catch (final NoSuchFieldException ignored) {
|
||||
}
|
||||
}
|
||||
// Register our service
|
||||
Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal);
|
||||
if (!found) {
|
||||
// We are the first!
|
||||
startSubmitting();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the data to the bStats server.
|
||||
*
|
||||
* @param plugin Any plugin. It's just used to get a logger instance.
|
||||
* @param data The data to send.
|
||||
* @throws Exception If the request failed.
|
||||
*/
|
||||
private static void sendData(final Plugin plugin, final JsonObject data) throws Exception {
|
||||
if (data == null) {
|
||||
throw new IllegalArgumentException("Data cannot be null!");
|
||||
}
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
throw new IllegalAccessException("This method must not be called from the main thread!");
|
||||
}
|
||||
if (logSentData) {
|
||||
plugin.getLogger().info("Sending data to bStats: " + data);
|
||||
}
|
||||
final HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
|
||||
|
||||
// Compress the data to save bandwidth
|
||||
final byte[] compressedData = compress(data.toString());
|
||||
|
||||
// Add headers
|
||||
connection.setRequestMethod("POST");
|
||||
connection.addRequestProperty("Accept", "application/json");
|
||||
connection.addRequestProperty("Connection", "close");
|
||||
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
|
||||
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
|
||||
connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
|
||||
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
|
||||
|
||||
// Send data
|
||||
connection.setDoOutput(true);
|
||||
try (final DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
|
||||
outputStream.write(compressedData);
|
||||
}
|
||||
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
try (final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
builder.append(line);
|
||||
}
|
||||
}
|
||||
|
||||
if (logResponseStatusText) {
|
||||
plugin.getLogger().info("Sent data to bStats and received response: " + builder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gzips the given String.
|
||||
*
|
||||
* @param str The string to gzip.
|
||||
* @return The gzipped String.
|
||||
* @throws IOException If the compression failed.
|
||||
*/
|
||||
private static byte[] compress(final String str) throws IOException {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
try (final GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) {
|
||||
gzip.write(str.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if bStats is enabled.
|
||||
*
|
||||
* @return Whether bStats is enabled or not.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom chart.
|
||||
*
|
||||
* @param chart The chart to add.
|
||||
*/
|
||||
public void addCustomChart(final CustomChart chart) {
|
||||
if (chart == null) {
|
||||
throw new IllegalArgumentException("Chart cannot be null!");
|
||||
}
|
||||
charts.add(chart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the Scheduler which submits our data every 30 minutes.
|
||||
*/
|
||||
private void startSubmitting() {
|
||||
final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
|
||||
timer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!plugin.isEnabled()) { // Plugin was disabled
|
||||
timer.cancel();
|
||||
return;
|
||||
}
|
||||
// Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler
|
||||
// Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;)
|
||||
Bukkit.getScheduler().runTask(plugin, () -> submitData());
|
||||
}
|
||||
}, 1000 * 60 * 5, 1000 * 60 * 30);
|
||||
// Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start
|
||||
// WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
|
||||
// WARNING: Just don't do it!
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plugin specific data.
|
||||
* This method is called using Reflection.
|
||||
*
|
||||
* @return The plugin specific data.
|
||||
*/
|
||||
public JsonObject getPluginData() {
|
||||
final JsonObject data = new JsonObject();
|
||||
|
||||
final String pluginName = plugin.getDescription().getName().replace("Essentials", "EssentialsX");
|
||||
final String pluginVersion = plugin.getDescription().getVersion();
|
||||
|
||||
data.addProperty("pluginName", pluginName); // Append the name of the plugin
|
||||
data.addProperty("id", pluginId); // Append the id of the plugin
|
||||
data.addProperty("pluginVersion", pluginVersion); // Append the version of the plugin
|
||||
final JsonArray customCharts = new JsonArray();
|
||||
for (final CustomChart customChart : charts) {
|
||||
// Add the data of the custom charts
|
||||
final JsonObject chart = customChart.getRequestJsonObject();
|
||||
if (chart == null) { // If the chart is null, we skip it
|
||||
continue;
|
||||
}
|
||||
customCharts.add(chart);
|
||||
}
|
||||
data.add("customCharts", customCharts);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server specific data.
|
||||
*
|
||||
* @return The server specific data.
|
||||
*/
|
||||
private JsonObject getServerData() {
|
||||
// Minecraft specific data
|
||||
int playerAmount;
|
||||
try {
|
||||
// Around MC 1.8 the return type was changed to a collection from an array,
|
||||
// This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
|
||||
final Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
|
||||
playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class)
|
||||
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
|
||||
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
|
||||
} catch (final Exception e) {
|
||||
playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed
|
||||
}
|
||||
final int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
|
||||
final String bukkitVersion = Bukkit.getVersion();
|
||||
final String bukkitName = Bukkit.getName();
|
||||
|
||||
// OS/Java specific data
|
||||
final String javaVersion = System.getProperty("java.version");
|
||||
final String osName = System.getProperty("os.name");
|
||||
final String osArch = System.getProperty("os.arch");
|
||||
final String osVersion = System.getProperty("os.version");
|
||||
final int coreCount = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
final JsonObject data = new JsonObject();
|
||||
|
||||
data.addProperty("serverUUID", serverUUID);
|
||||
|
||||
data.addProperty("playerAmount", playerAmount);
|
||||
data.addProperty("onlineMode", onlineMode);
|
||||
data.addProperty("bukkitVersion", bukkitVersion);
|
||||
data.addProperty("bukkitName", bukkitName);
|
||||
|
||||
data.addProperty("javaVersion", javaVersion);
|
||||
data.addProperty("osName", osName);
|
||||
data.addProperty("osArch", osArch);
|
||||
data.addProperty("osVersion", osVersion);
|
||||
data.addProperty("coreCount", coreCount);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the data and sends it afterwards.
|
||||
*/
|
||||
private void submitData() {
|
||||
final JsonObject data = getServerData();
|
||||
|
||||
final JsonArray pluginData = new JsonArray();
|
||||
// Search for all other bStats Metrics classes to get their plugin data
|
||||
for (final Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
|
||||
try {
|
||||
service.getField("B_STATS_VERSION"); // Our identifier :)
|
||||
|
||||
for (final RegisteredServiceProvider<?> provider : Bukkit.getServicesManager().getRegistrations(service)) {
|
||||
try {
|
||||
final Object plugin = provider.getService().getMethod("getPluginData").invoke(provider.getProvider());
|
||||
if (plugin instanceof JsonObject) {
|
||||
pluginData.add((JsonObject) plugin);
|
||||
} else { // old bstats version compatibility
|
||||
try {
|
||||
final Class<?> jsonObjectJsonSimple = Class.forName("org.json.simple.JSONObject");
|
||||
if (plugin.getClass().isAssignableFrom(jsonObjectJsonSimple)) {
|
||||
final Method jsonStringGetter = jsonObjectJsonSimple.getDeclaredMethod("toJSONString");
|
||||
jsonStringGetter.setAccessible(true);
|
||||
final String jsonString = (String) jsonStringGetter.invoke(plugin);
|
||||
final JsonObject object = new JsonParser().parse(jsonString).getAsJsonObject();
|
||||
pluginData.add(object);
|
||||
}
|
||||
} catch (final ClassNotFoundException e) {
|
||||
// minecraft version 1.14+
|
||||
if (logFailedRequests) {
|
||||
this.plugin.getLogger().log(Level.SEVERE, "Encountered unexpected exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (final NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
|
||||
}
|
||||
}
|
||||
} catch (final NoSuchFieldException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
data.add("plugins", pluginData);
|
||||
|
||||
// Create a new thread for the connection to the bStats server
|
||||
new Thread(() -> {
|
||||
try {
|
||||
// Send the data
|
||||
sendData(plugin, data);
|
||||
} catch (final Exception e) {
|
||||
// Something went wrong! :(
|
||||
if (logFailedRequests) {
|
||||
plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom chart.
|
||||
*/
|
||||
public static abstract class CustomChart {
|
||||
|
||||
// The id of the chart
|
||||
final String chartId;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
*/
|
||||
CustomChart(final String chartId) {
|
||||
if (chartId == null || chartId.isEmpty()) {
|
||||
throw new IllegalArgumentException("ChartId cannot be null or empty!");
|
||||
}
|
||||
this.chartId = chartId;
|
||||
}
|
||||
|
||||
private JsonObject getRequestJsonObject() {
|
||||
final JsonObject chart = new JsonObject();
|
||||
chart.addProperty("chartId", chartId);
|
||||
try {
|
||||
final JsonObject data = getChartData();
|
||||
if (data == null) {
|
||||
// If the data is null we don't send the chart.
|
||||
return null;
|
||||
}
|
||||
chart.add("data", data);
|
||||
} catch (final Throwable t) {
|
||||
if (logFailedRequests) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return chart;
|
||||
}
|
||||
|
||||
protected abstract JsonObject getChartData() throws Exception;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom simple pie.
|
||||
*/
|
||||
public static class SimplePie extends CustomChart {
|
||||
|
||||
private final Callable<String> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimplePie(final String chartId, final Callable<String> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObject getChartData() throws Exception {
|
||||
final JsonObject data = new JsonObject();
|
||||
final String value = callable.call();
|
||||
if (value == null || value.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.addProperty("value", value);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom advanced pie.
|
||||
*/
|
||||
public static class AdvancedPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedPie(final String chartId, final Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObject getChartData() throws Exception {
|
||||
final JsonObject data = new JsonObject();
|
||||
final JsonObject values = new JsonObject();
|
||||
final Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (final Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
values.addProperty(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.add("values", values);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom drilldown pie.
|
||||
*/
|
||||
public static class DrilldownPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Map<String, Integer>>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public DrilldownPie(final String chartId, final Callable<Map<String, Map<String, Integer>>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject getChartData() throws Exception {
|
||||
final JsonObject data = new JsonObject();
|
||||
final JsonObject values = new JsonObject();
|
||||
final Map<String, Map<String, Integer>> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean reallyAllSkipped = true;
|
||||
for (final Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
|
||||
final JsonObject value = new JsonObject();
|
||||
boolean allSkipped = true;
|
||||
for (final Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
|
||||
value.addProperty(valueEntry.getKey(), valueEntry.getValue());
|
||||
allSkipped = false;
|
||||
}
|
||||
if (!allSkipped) {
|
||||
reallyAllSkipped = false;
|
||||
values.add(entryValues.getKey(), value);
|
||||
}
|
||||
}
|
||||
if (reallyAllSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.add("values", values);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom single line chart.
|
||||
*/
|
||||
public static class SingleLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Integer> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SingleLineChart(final String chartId, final Callable<Integer> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObject getChartData() throws Exception {
|
||||
final JsonObject data = new JsonObject();
|
||||
final int value = callable.call();
|
||||
if (value == 0) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.addProperty("value", value);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom multi line chart.
|
||||
*/
|
||||
public static class MultiLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public MultiLineChart(final String chartId, final Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObject getChartData() throws Exception {
|
||||
final JsonObject data = new JsonObject();
|
||||
final JsonObject values = new JsonObject();
|
||||
final Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (final Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
values.addProperty(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.add("values", values);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom simple bar chart.
|
||||
*/
|
||||
public static class SimpleBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimpleBarChart(final String chartId, final Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObject getChartData() throws Exception {
|
||||
final JsonObject data = new JsonObject();
|
||||
final JsonObject values = new JsonObject();
|
||||
final Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
for (final Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
final JsonArray categoryValues = new JsonArray();
|
||||
categoryValues.add(new JsonPrimitive(entry.getValue()));
|
||||
values.add(entry.getKey(), categoryValues);
|
||||
}
|
||||
data.add("values", values);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom advanced bar chart.
|
||||
*/
|
||||
public static class AdvancedBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, int[]>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedBarChart(final String chartId, final Callable<Map<String, int[]>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonObject getChartData() throws Exception {
|
||||
final JsonObject data = new JsonObject();
|
||||
final JsonObject values = new JsonObject();
|
||||
final Map<String, int[]> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (final Map.Entry<String, int[]> entry : map.entrySet()) {
|
||||
if (entry.getValue().length == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
final JsonArray categoryValues = new JsonArray();
|
||||
for (final int categoryValue : entry.getValue()) {
|
||||
categoryValues.add(new JsonPrimitive(categoryValue));
|
||||
}
|
||||
values.add(entry.getKey(), categoryValues);
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.add("values", values);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
package com.earth2me.essentials.metrics;
|
||||
|
||||
import com.earth2me.essentials.Essentials;
|
||||
import com.earth2me.essentials.register.payment.Methods;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class MetricsWrapper {
|
||||
|
||||
private static final List<String> KNOWN_FORCED_METRICS = ImmutableList.of("ChatControl");
|
||||
private static boolean hasWarned = false;
|
||||
private final Essentials ess;
|
||||
private final Metrics metrics;
|
||||
private final Map<String, Boolean> commands = new HashMap<>();
|
||||
private final Plugin plugin;
|
||||
|
||||
public MetricsWrapper(final Plugin plugin, final int pluginId, final boolean includeCommands) {
|
||||
this.plugin = plugin;
|
||||
this.ess = (Essentials) Bukkit.getPluginManager().getPlugin("Essentials");
|
||||
this.metrics = new Metrics(plugin, pluginId);
|
||||
|
||||
if (metrics.isEnabled()) {
|
||||
plugin.getLogger().info("Starting Metrics. Opt-out using the global bStats config.");
|
||||
} else {
|
||||
plugin.getLogger().info("Metrics disabled per bStats config.");
|
||||
}
|
||||
|
||||
checkForcedMetrics();
|
||||
addPermsChart();
|
||||
addEconomyChart();
|
||||
|
||||
// bStats' backend currently doesn't support multi-line charts or advanced bar charts
|
||||
// These are included for when bStats is ready to accept this data
|
||||
addVersionHistoryChart();
|
||||
if (includeCommands) addCommandsChart();
|
||||
}
|
||||
|
||||
public void markCommand(final String command, final boolean state) {
|
||||
commands.put(command, state);
|
||||
}
|
||||
|
||||
public void addCustomChart(final Metrics.CustomChart chart) {
|
||||
metrics.addCustomChart(chart);
|
||||
}
|
||||
|
||||
private void addPermsChart() {
|
||||
metrics.addCustomChart(new Metrics.DrilldownPie("permsPlugin", () -> {
|
||||
final Map<String, Map<String, Integer>> result = new HashMap<>();
|
||||
final String handler = ess.getPermissionsHandler().getName();
|
||||
final Map<String, Integer> backend = new HashMap<>();
|
||||
backend.put(ess.getPermissionsHandler().getBackendName(), 1);
|
||||
result.put(handler, backend);
|
||||
return result;
|
||||
}));
|
||||
}
|
||||
|
||||
private void addEconomyChart() {
|
||||
metrics.addCustomChart(new Metrics.DrilldownPie("econPlugin", () -> {
|
||||
final Map<String, Map<String, Integer>> result = new HashMap<>();
|
||||
final Map<String, Integer> backend = new HashMap<>();
|
||||
if (Methods.hasMethod()) {
|
||||
backend.put(Methods.getMethod().getBackend(), 1);
|
||||
result.put(Methods.getMethod().getName(), backend);
|
||||
} else {
|
||||
backend.put("Essentials", 1);
|
||||
result.put("Essentials", backend);
|
||||
}
|
||||
return result;
|
||||
}));
|
||||
}
|
||||
|
||||
private void addVersionHistoryChart() {
|
||||
metrics.addCustomChart(new Metrics.MultiLineChart("versionHistory", () -> {
|
||||
final HashMap<String, Integer> result = new HashMap<>();
|
||||
result.put(plugin.getDescription().getVersion(), 1);
|
||||
return result;
|
||||
}));
|
||||
}
|
||||
|
||||
private void addCommandsChart() {
|
||||
for (final String command : plugin.getDescription().getCommands().keySet()) {
|
||||
markCommand(command, false);
|
||||
}
|
||||
|
||||
metrics.addCustomChart(new Metrics.AdvancedBarChart("commands", () -> {
|
||||
final Map<String, int[]> result = new HashMap<>();
|
||||
for (final Map.Entry<String, Boolean> entry : commands.entrySet()) {
|
||||
if (entry.getValue()) {
|
||||
result.put(entry.getKey(), new int[] {1, 0});
|
||||
} else {
|
||||
result.put(entry.getKey(), new int[] {0, 1});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}));
|
||||
}
|
||||
|
||||
private void checkForcedMetrics() {
|
||||
if (hasWarned) return;
|
||||
hasWarned = true;
|
||||
|
||||
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> {
|
||||
for (final Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
|
||||
try {
|
||||
service.getField("B_STATS_VERSION"); // Identifies bStats classes
|
||||
|
||||
final JavaPlugin owningPlugin = getProvidingPlugin(service);
|
||||
if (owningPlugin != null && KNOWN_FORCED_METRICS.contains(owningPlugin.getName())) {
|
||||
warnForcedMetrics(service);
|
||||
} else {
|
||||
try {
|
||||
service.getDeclaredField("pluginId"); // Only present in recent bStats classes, which should also have the enabled field unless modified
|
||||
} catch (final NoSuchFieldException e) {
|
||||
// Old bStats class found so "enabled" field detection is unreliable.
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
service.getDeclaredField("enabled"); // In modified forced metrics classes, this will fail
|
||||
} catch (final NoSuchFieldException e) {
|
||||
warnForcedMetrics(service);
|
||||
}
|
||||
}
|
||||
} catch (final NoSuchFieldException ignored) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void warnForcedMetrics(final Class<?> service) {
|
||||
final Plugin servicePlugin = JavaPlugin.getProvidingPlugin(service);
|
||||
plugin.getLogger().severe("WARNING: Potential forced metrics collection by plugin '" + servicePlugin.getName() + "' v" + servicePlugin.getDescription().getVersion());
|
||||
plugin.getLogger().severe("Your server is running a plugin that may not respect bStats' opt-out settings.");
|
||||
plugin.getLogger().severe("This may cause data to be uploaded to bStats.org for plugins that use bStats, even if you've opted out in the bStats config.");
|
||||
plugin.getLogger().severe("Please report this to bStats and to the authors of '" + servicePlugin.getName() + "'. (Offending class: " + service.getName() + ")");
|
||||
}
|
||||
|
||||
private JavaPlugin getProvidingPlugin(final Class<?> clazz) {
|
||||
try {
|
||||
return JavaPlugin.getProvidingPlugin(clazz);
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
|
||||
final ClassLoader parent = clazz.getClassLoader().getParent();
|
||||
if (parent.getClass().getName().equals("org.bukkit.plugin.java.PluginClassLoader")) {
|
||||
try {
|
||||
final Field pluginField = parent.getClass().getDeclaredField("plugin");
|
||||
pluginField.setAccessible(true);
|
||||
return (JavaPlugin) pluginField.get(parent);
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.earth2me.essentials.perm;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface IPermissionsHandler {
|
||||
String getGroup(Player base);
|
||||
|
||||
List<String> getGroups(Player base);
|
||||
|
||||
boolean canBuild(Player base, String group);
|
||||
|
||||
boolean inGroup(Player base, String group);
|
||||
|
||||
boolean hasPermission(Player base, String node);
|
||||
|
||||
// Does not check for * permissions
|
||||
boolean isPermissionSet(Player base, String node);
|
||||
|
||||
String getPrefix(Player base);
|
||||
|
||||
String getSuffix(Player base);
|
||||
|
||||
void registerContext(String context, Function<Player, Iterable<String>> calculator, Supplier<Iterable<String>> suggestions);
|
||||
|
||||
void unregisterContexts();
|
||||
|
||||
String getBackendName();
|
||||
|
||||
boolean tryProvider();
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package com.earth2me.essentials.perm.impl;
|
||||
|
||||
import net.luckperms.api.LuckPerms;
|
||||
import net.luckperms.api.context.ContextCalculator;
|
||||
import net.luckperms.api.context.ContextConsumer;
|
||||
import net.luckperms.api.context.ContextSet;
|
||||
import net.luckperms.api.context.ImmutableContextSet;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class LuckPermsHandler extends ModernVaultHandler {
|
||||
private LuckPerms luckPerms;
|
||||
private Set<ContextCalculator<Player>> contextCalculators;
|
||||
|
||||
@Override
|
||||
public void registerContext(final String context, final Function<Player, Iterable<String>> calculator, final Supplier<Iterable<String>> suggestions) {
|
||||
final ContextCalculator<Player> contextCalculator = new ContextCalculator<Player>() {
|
||||
@Override
|
||||
public void calculate(final Player target, final ContextConsumer consumer) {
|
||||
calculator.apply(target).forEach(value -> consumer.accept(context, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContextSet estimatePotentialContexts() {
|
||||
final ImmutableContextSet.Builder builder = ImmutableContextSet.builder();
|
||||
suggestions.get().forEach(value -> builder.add(context, value));
|
||||
return builder.build();
|
||||
}
|
||||
};
|
||||
luckPerms.getContextManager().registerCalculator(contextCalculator);
|
||||
contextCalculators.add(contextCalculator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterContexts() {
|
||||
contextCalculators.forEach(contextCalculator -> luckPerms.getContextManager().unregisterCalculator(contextCalculator));
|
||||
contextCalculators.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryProvider() {
|
||||
final RegisteredServiceProvider<LuckPerms> provider = Bukkit.getServicesManager().getRegistration(LuckPerms.class);
|
||||
if (provider != null) {
|
||||
luckPerms = provider.getProvider();
|
||||
contextCalculators = new HashSet<>();
|
||||
}
|
||||
return luckPerms != null && super.tryProvider();
|
||||
}
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
package com.earth2me.essentials.register.payment;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by a payment method.
|
||||
* Copyright (C) 2011
|
||||
* AOL license <http://aol.nexua.org>
|
||||
* <p>
|
||||
* For more information about the licensing of this code in EssentialsX, see below:
|
||||
* https://gist.github.com/md678685/0935441c9573c030c8bd5a2e604aeec3
|
||||
*
|
||||
* @author Nijikokun <nijikokun@shortmail.com> (@nijikokun)
|
||||
*/
|
||||
public interface Method {
|
||||
/**
|
||||
* Encodes the Plugin into an Object disguised as the Plugin. If you want the original Plugin Class you must cast it
|
||||
* to the correct Plugin, to do so you have to verify the name and or version then cast.
|
||||
* <p/>
|
||||
* <pre>
|
||||
* if(method.getName().equalsIgnoreCase("iConomy"))
|
||||
* iConomy plugin = ((iConomy)method.getPlugin());</pre>
|
||||
*
|
||||
* @return <code>Object</code>
|
||||
* @see #getName()
|
||||
* @see #getVersion()
|
||||
*/
|
||||
Plugin getPlugin();
|
||||
|
||||
/**
|
||||
* Set Plugin data.
|
||||
*
|
||||
* @param plugin Plugin
|
||||
*/
|
||||
void setPlugin(Plugin plugin);
|
||||
|
||||
/**
|
||||
* Returns the actual name of this method.
|
||||
*
|
||||
* @return <code>String</code> Plugin name.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Returns the backend plugin of this economy method, if applicable.
|
||||
*
|
||||
* @return <code>String</code> Plugin name.
|
||||
*/
|
||||
String getBackend();
|
||||
|
||||
/**
|
||||
* Returns the reported name of this method.
|
||||
*
|
||||
* @return <code>String</code> Plugin name.
|
||||
*/
|
||||
String getLongName();
|
||||
|
||||
/**
|
||||
* Returns the actual version of this method.
|
||||
*
|
||||
* @return <code>String</code> Plugin version.
|
||||
*/
|
||||
String getVersion();
|
||||
|
||||
/**
|
||||
* Returns the amount of decimal places that get stored NOTE: it will return -1 if there is no rounding
|
||||
*
|
||||
* @return <code>int</code> for each decimal place
|
||||
*/
|
||||
int fractionalDigits();
|
||||
|
||||
/**
|
||||
* Formats amounts into this payment methods style of currency display.
|
||||
*
|
||||
* @param amount Double
|
||||
* @return <code>String</code> - Formatted Currency Display.
|
||||
*/
|
||||
String format(double amount);
|
||||
|
||||
/**
|
||||
* Allows the verification of bank API existence in this payment method.
|
||||
*
|
||||
* @return <code>boolean</code>
|
||||
*/
|
||||
boolean hasBanks();
|
||||
|
||||
/**
|
||||
* Determines the existence of a bank via name.
|
||||
*
|
||||
* @param bank Bank name
|
||||
* @return <code>boolean</code>
|
||||
* @see #hasBanks
|
||||
*/
|
||||
boolean hasBank(String bank);
|
||||
|
||||
/**
|
||||
* Determines the existence of an account via name.
|
||||
*
|
||||
* @param name Account name
|
||||
* @return <code>boolean</code>
|
||||
*/
|
||||
boolean hasAccount(String name);
|
||||
|
||||
/**
|
||||
* Check to see if an account <code>name</code> is tied to a <code>bank</code>.
|
||||
*
|
||||
* @param bank Bank name
|
||||
* @param name Account name
|
||||
* @return <code>boolean</code>
|
||||
*/
|
||||
boolean hasBankAccount(String bank, String name);
|
||||
|
||||
/**
|
||||
* Forces an account creation
|
||||
*
|
||||
* @param name Account name
|
||||
* @return <code>boolean</code>
|
||||
*/
|
||||
boolean createAccount(String name);
|
||||
|
||||
/**
|
||||
* Forces an account creation
|
||||
*
|
||||
* @param name Account name
|
||||
* @param balance Initial account balance
|
||||
* @return <code>boolean</code>
|
||||
*/
|
||||
boolean createAccount(String name, Double balance);
|
||||
|
||||
/**
|
||||
* Returns a <code>MethodAccount</code> class for an account <code>name</code>.
|
||||
*
|
||||
* @param name Account name
|
||||
* @return <code>MethodAccount</code> <em>or</em> <code>Null</code>
|
||||
*/
|
||||
MethodAccount getAccount(String name);
|
||||
|
||||
/**
|
||||
* Returns a <code>MethodBankAccount</code> class for an account <code>name</code>.
|
||||
*
|
||||
* @param bank Bank name
|
||||
* @param name Account name
|
||||
* @return <code>MethodBankAccount</code> <em>or</em> <code>Null</code>
|
||||
*/
|
||||
MethodBankAccount getBankAccount(String bank, String name);
|
||||
|
||||
/**
|
||||
* Checks to verify the compatibility between this Method and a plugin. Internal usage only, for the most part.
|
||||
*
|
||||
* @param plugin Plugin
|
||||
* @return <code>boolean</code>
|
||||
*/
|
||||
boolean isCompatible(Plugin plugin);
|
||||
|
||||
/**
|
||||
* Contains Calculator and Balance functions for Accounts.
|
||||
*/
|
||||
interface MethodAccount {
|
||||
double balance();
|
||||
|
||||
boolean set(double amount);
|
||||
|
||||
boolean add(double amount);
|
||||
|
||||
boolean subtract(double amount);
|
||||
|
||||
boolean multiply(double amount);
|
||||
|
||||
boolean divide(double amount);
|
||||
|
||||
boolean hasEnough(double amount);
|
||||
|
||||
boolean hasOver(double amount);
|
||||
|
||||
boolean hasUnder(double amount);
|
||||
|
||||
boolean isNegative();
|
||||
|
||||
boolean remove();
|
||||
|
||||
@Override
|
||||
String toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains Calculator and Balance functions for Bank Accounts.
|
||||
*/
|
||||
interface MethodBankAccount {
|
||||
double balance();
|
||||
|
||||
String getBankName();
|
||||
|
||||
int getBankId();
|
||||
|
||||
boolean set(double amount);
|
||||
|
||||
boolean add(double amount);
|
||||
|
||||
boolean subtract(double amount);
|
||||
|
||||
boolean multiply(double amount);
|
||||
|
||||
boolean divide(double amount);
|
||||
|
||||
boolean hasEnough(double amount);
|
||||
|
||||
boolean hasOver(double amount);
|
||||
|
||||
boolean hasUnder(double amount);
|
||||
|
||||
boolean isNegative();
|
||||
|
||||
boolean remove();
|
||||
|
||||
@Override
|
||||
String toString();
|
||||
}
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
package com.earth2me.essentials.register.payment;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The <code>Methods</code> initializes Methods that utilize the Method interface based on a "first come, first served"
|
||||
* basis.
|
||||
* <p/>
|
||||
* Allowing you to check whether a payment method exists or not.
|
||||
* <p/>
|
||||
* Methods also allows you to set a preferred method of payment before it captures payment plugins in the initialization
|
||||
* process.
|
||||
* <p/>
|
||||
* in
|
||||
* <code>bukkit.yml</code>: <blockquote><pre>
|
||||
* economy:
|
||||
* preferred: "iConomy"
|
||||
* </pre></blockquote>
|
||||
* <p>
|
||||
* For more information about the licensing of this code in EssentialsX, see below:
|
||||
* https://gist.github.com/md678685/0935441c9573c030c8bd5a2e604aeec3
|
||||
*
|
||||
* @author Nijikokun <nijikokun@shortmail.com> (@nijikokun) @copyright: Copyright (C) 2011 @license: AOL license
|
||||
* <http://aol.nexua.org>
|
||||
*/
|
||||
public final class Methods {
|
||||
private static final Set<Method> Methods = new HashSet<>();
|
||||
private static final Set<String> Dependencies = new HashSet<>();
|
||||
private static final Set<Method> Attachables = new HashSet<>();
|
||||
private static String version = null;
|
||||
private static boolean self = false;
|
||||
private static Method Method = null;
|
||||
private static String preferred = "";
|
||||
|
||||
private Methods() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement all methods along with their respective name & class.
|
||||
*/
|
||||
public static void init() {
|
||||
if (!Methods.isEmpty()) {
|
||||
throw new IllegalStateException("Methods already initialised!");
|
||||
}
|
||||
|
||||
addMethod("Vault", new com.earth2me.essentials.register.payment.methods.VaultEco());
|
||||
}
|
||||
|
||||
/**
|
||||
* Use to reset methods during disable
|
||||
*/
|
||||
public static void reset() {
|
||||
version = null;
|
||||
self = false;
|
||||
Method = null;
|
||||
preferred = "";
|
||||
Attachables.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use to get version of Register plugin
|
||||
*
|
||||
* @return version
|
||||
*/
|
||||
public static String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the plugin to setup version
|
||||
*
|
||||
* @param v version
|
||||
*/
|
||||
public static void setVersion(final String v) {
|
||||
version = v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of payment method names that have been loaded through the <code>_init</code> method.
|
||||
*
|
||||
* @return <code>Set<String></code> - Array of payment methods that are loaded.
|
||||
* @see #setMethod(PluginManager)
|
||||
*/
|
||||
public static Set<String> getDependencies() {
|
||||
return Dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interprets Plugin class data to verify whether it is compatible with an existing payment method to use for
|
||||
* payments and other various economic activity.
|
||||
*
|
||||
* @param plugin Plugin data from bukkit, Internal Class file.
|
||||
* @return Method <em>or</em> Null
|
||||
*/
|
||||
public static Method createMethod(final Plugin plugin) {
|
||||
for (final Method method : Methods) {
|
||||
if (method.isCompatible(plugin)) {
|
||||
method.setPlugin(plugin);
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void addMethod(final String name, final Method method) {
|
||||
Dependencies.add(name);
|
||||
Methods.add(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies if Register has set a payment method for usage yet.
|
||||
*
|
||||
* @return <code>boolean</code>
|
||||
* @see #setMethod(PluginManager)
|
||||
* @see #checkDisabled(Plugin)
|
||||
*/
|
||||
public static boolean hasMethod() {
|
||||
return Method != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks Plugin Class against a multitude of checks to verify it's usability as a payment method.
|
||||
*
|
||||
* @param manager the plugin manager for the server
|
||||
* @return <code>boolean</code> True on success, False on failure.
|
||||
*/
|
||||
public static boolean setMethod(final PluginManager manager) {
|
||||
if (hasMethod()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (self) {
|
||||
self = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
boolean match = false;
|
||||
Plugin plugin;
|
||||
|
||||
for (final String name : getDependencies()) {
|
||||
if (hasMethod()) {
|
||||
break;
|
||||
}
|
||||
|
||||
plugin = manager.getPlugin(name);
|
||||
if (plugin == null || !plugin.isEnabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final Method current = createMethod(plugin);
|
||||
if (current == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (preferred.isEmpty()) {
|
||||
Method = current;
|
||||
} else {
|
||||
Attachables.add(current);
|
||||
}
|
||||
}
|
||||
|
||||
if (!preferred.isEmpty()) {
|
||||
do {
|
||||
if (hasMethod()) {
|
||||
match = true;
|
||||
} else {
|
||||
for (final Method attached : Attachables) {
|
||||
if (attached == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hasMethod()) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (preferred.isEmpty()) {
|
||||
Method = attached;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
if (preferred.equalsIgnoreCase(attached.getName())) {
|
||||
Method = attached;
|
||||
} else {
|
||||
Method = attached;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
} while (!match);
|
||||
}
|
||||
|
||||
return hasMethod();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the preferred economy
|
||||
*
|
||||
* @return <code>boolean</code>
|
||||
*/
|
||||
public static boolean setPreferred(final String check) {
|
||||
if (getDependencies().contains(check)) {
|
||||
preferred = check;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grab the existing and initialized (hopefully) Method Class.
|
||||
*
|
||||
* @return <code>Method</code> <em>or</em> <code>Null</code>
|
||||
*/
|
||||
public static Method getMethod() {
|
||||
return Method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify is a plugin is disabled, only does this if we there is an existing payment method initialized in
|
||||
* Register.
|
||||
*
|
||||
* @param method Plugin data from bukkit, Internal Class file.
|
||||
* @return <code>boolean</code>
|
||||
*/
|
||||
public static boolean checkDisabled(final Plugin method) {
|
||||
if (!hasMethod()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Method.isCompatible(method)) {
|
||||
Method = null;
|
||||
}
|
||||
|
||||
return Method == null;
|
||||
}
|
||||
}
|
||||
@@ -1,280 +0,0 @@
|
||||
package com.earth2me.essentials.register.payment.methods;
|
||||
|
||||
import com.earth2me.essentials.register.payment.Method;
|
||||
import net.milkbowl.vault.economy.Economy;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
|
||||
public class VaultEco implements Method {
|
||||
private Plugin vault;
|
||||
private Economy economy;
|
||||
|
||||
@Override
|
||||
public Plugin getPlugin() {
|
||||
return this.vault;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlugin(final Plugin plugin) {
|
||||
this.vault = plugin;
|
||||
final RegisteredServiceProvider<Economy> economyProvider = this.vault.getServer().getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class);
|
||||
if (economyProvider != null) {
|
||||
this.economy = economyProvider.getProvider();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean createAccount(final String name, final Double amount) {
|
||||
if (hasAccount(name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.vault.getDescription().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBackend() {
|
||||
return economy == null ? "NoEco" : economy.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLongName() {
|
||||
return getName().concat(" - Economy: ").concat(getBackend());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return this.vault.getDescription().getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int fractionalDigits() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(final double amount) {
|
||||
return this.economy.format(amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBanks() {
|
||||
return this.economy.hasBankSupport();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBank(final String bank) {
|
||||
return this.economy.getBanks().contains(bank);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAccount(final String name) {
|
||||
return this.economy.hasAccount(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBankAccount(final String bank, final String name) {
|
||||
return this.economy.isBankOwner(bank, name).transactionSuccess() || this.economy.isBankMember(bank, name).transactionSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean createAccount(final String name) {
|
||||
return this.economy.createBank(name, "").transactionSuccess();
|
||||
}
|
||||
|
||||
public boolean createAccount(final String name, final double balance) {
|
||||
if (!this.economy.createBank(name, "").transactionSuccess()) {
|
||||
return false;
|
||||
}
|
||||
return this.economy.bankDeposit(name, balance).transactionSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodAccount getAccount(final String name) {
|
||||
if (!hasAccount(name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new VaultAccount(name, this.economy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodBankAccount getBankAccount(final String bank, final String name) {
|
||||
if (!hasBankAccount(bank, name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new VaultBankAccount(bank, economy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompatible(final Plugin plugin) {
|
||||
try {
|
||||
final RegisteredServiceProvider<Economy> ecoPlugin = plugin.getServer().getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class);
|
||||
return plugin.getName().equals("Vault") && ecoPlugin != null && !ecoPlugin.getProvider().getName().equals("Essentials Economy");
|
||||
} catch (final LinkageError | Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static class VaultAccount implements MethodAccount {
|
||||
private final String name;
|
||||
private final Economy economy;
|
||||
|
||||
VaultAccount(final String name, final Economy economy) {
|
||||
this.name = name;
|
||||
this.economy = economy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double balance() {
|
||||
return this.economy.getBalance(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean set(final double amount) {
|
||||
if (!this.economy.withdrawPlayer(this.name, this.balance()).transactionSuccess()) {
|
||||
return false;
|
||||
}
|
||||
if (amount == 0) {
|
||||
return true;
|
||||
}
|
||||
return this.economy.depositPlayer(this.name, amount).transactionSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(final double amount) {
|
||||
return this.economy.depositPlayer(this.name, amount).transactionSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean subtract(final double amount) {
|
||||
return this.economy.withdrawPlayer(this.name, amount).transactionSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean multiply(final double amount) {
|
||||
final double balance = this.balance();
|
||||
return this.set(balance * amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean divide(final double amount) {
|
||||
final double balance = this.balance();
|
||||
return this.set(balance / amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEnough(final double amount) {
|
||||
return this.balance() >= amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasOver(final double amount) {
|
||||
return this.balance() > amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasUnder(final double amount) {
|
||||
return this.balance() < amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNegative() {
|
||||
return this.balance() < 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove() {
|
||||
return this.set(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
public static class VaultBankAccount implements MethodBankAccount {
|
||||
private final String bank;
|
||||
private final Economy economy;
|
||||
|
||||
public VaultBankAccount(final String bank, final Economy economy) {
|
||||
this.bank = bank;
|
||||
this.economy = economy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBankName() {
|
||||
return this.bank;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBankId() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double balance() {
|
||||
return this.economy.bankBalance(this.bank).balance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean set(final double amount) {
|
||||
if (!this.economy.bankWithdraw(this.bank, this.balance()).transactionSuccess()) {
|
||||
return false;
|
||||
}
|
||||
if (amount == 0) {
|
||||
return true;
|
||||
}
|
||||
return this.economy.bankDeposit(this.bank, amount).transactionSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(final double amount) {
|
||||
return this.economy.bankDeposit(this.bank, amount).transactionSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean subtract(final double amount) {
|
||||
return this.economy.bankWithdraw(this.bank, amount).transactionSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean multiply(final double amount) {
|
||||
final double balance = this.balance();
|
||||
return this.set(balance * amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean divide(final double amount) {
|
||||
final double balance = this.balance();
|
||||
return this.set(balance / amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEnough(final double amount) {
|
||||
return this.balance() >= amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasOver(final double amount) {
|
||||
return this.balance() > amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasUnder(final double amount) {
|
||||
return this.balance() < amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNegative() {
|
||||
return this.balance() < 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove() {
|
||||
return this.set(0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.earth2me.essentials.settings;
|
||||
|
||||
import com.earth2me.essentials.storage.MapValueType;
|
||||
import com.earth2me.essentials.storage.StorageObject;
|
||||
import org.bukkit.Location;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Jails implements StorageObject {
|
||||
@MapValueType(Location.class)
|
||||
private Map<String, Location> jails = new HashMap<>();
|
||||
|
||||
public Map<String, Location> getJails() {
|
||||
return jails;
|
||||
}
|
||||
|
||||
public void setJails(final Map<String, Location> jails) {
|
||||
this.jails = jails;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final Jails jails1 = (Jails) o;
|
||||
return Objects.equals(jails, jails1.jails);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(jails);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.earth2me.essentials.settings;
|
||||
|
||||
import com.earth2me.essentials.storage.MapValueType;
|
||||
import com.earth2me.essentials.storage.StorageObject;
|
||||
import org.bukkit.Location;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Spawns implements StorageObject {
|
||||
@MapValueType(Location.class)
|
||||
private Map<String, Location> spawns = new HashMap<>();
|
||||
|
||||
public Map<String, Location> getSpawns() {
|
||||
return spawns;
|
||||
}
|
||||
|
||||
public void setSpawns(final Map<String, Location> spawns) {
|
||||
this.spawns = spawns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final Spawns spawns1 = (Spawns) o;
|
||||
return Objects.equals(spawns, spawns1.spawns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(spawns);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.earth2me.essentials.signs;
|
||||
|
||||
public class SignException extends Exception {
|
||||
public SignException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SignException(final String message, final Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package com.earth2me.essentials.signs;
|
||||
|
||||
import com.earth2me.essentials.User;
|
||||
import net.ess3.api.IEssentials;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class SignMail extends EssentialsSign {
|
||||
public SignMail() {
|
||||
super("Mail");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onSignInteract(final ISign sign, final User player, final String username, final IEssentials ess) throws SignException {
|
||||
final List<String> mail = player.getMails();
|
||||
if (mail.isEmpty()) {
|
||||
player.sendMessage(tl("noNewMail"));
|
||||
return false;
|
||||
}
|
||||
for (final String s : mail) {
|
||||
player.sendMessage(s);
|
||||
}
|
||||
player.sendMessage(tl("markMailAsRead"));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package com.earth2me.essentials.storage;
|
||||
|
||||
import net.ess3.api.IEssentials;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public abstract class AbstractDelayedYamlFileReader<T extends StorageObject> implements Runnable {
|
||||
protected final transient IEssentials plugin;
|
||||
private final transient File file;
|
||||
private final transient Class<T> clazz;
|
||||
|
||||
public AbstractDelayedYamlFileReader(final IEssentials ess, final File file, final Class<T> clazz) {
|
||||
this.file = file;
|
||||
this.clazz = clazz;
|
||||
this.plugin = ess;
|
||||
ess.runTaskAsynchronously(this);
|
||||
}
|
||||
|
||||
public abstract void onStart();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
onStart();
|
||||
try {
|
||||
final FileReader reader = new FileReader(file);
|
||||
try {
|
||||
final T object = new YamlStorageReader(reader, plugin).load(clazz);
|
||||
onSuccess(object);
|
||||
} finally {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (final IOException ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "File can't be closed: " + file.toString(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (final FileNotFoundException ex) {
|
||||
onException();
|
||||
if (plugin.getSettings() == null || plugin.getSettings().isDebug()) {
|
||||
Bukkit.getLogger().log(Level.INFO, "File not found: " + file.toString());
|
||||
}
|
||||
} catch (final ObjectLoadException ex) {
|
||||
onException();
|
||||
Bukkit.getLogger().log(Level.SEVERE, "File broken: " + file.toString(), ex.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void onSuccess(T object);
|
||||
|
||||
public abstract void onException();
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package com.earth2me.essentials.storage;
|
||||
|
||||
import net.ess3.api.IEssentials;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public abstract class AbstractDelayedYamlFileWriter implements Runnable {
|
||||
private final transient File file;
|
||||
|
||||
public AbstractDelayedYamlFileWriter(final IEssentials ess, final File file) {
|
||||
this.file = file;
|
||||
ess.runTaskAsynchronously(this);
|
||||
}
|
||||
|
||||
public abstract StorageObject getObject();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
PrintWriter pw = null;
|
||||
try {
|
||||
final StorageObject object = getObject();
|
||||
final File folder = file.getParentFile();
|
||||
if (!folder.exists()) {
|
||||
folder.mkdirs();
|
||||
}
|
||||
pw = new PrintWriter(file);
|
||||
new YamlStorageWriter(pw).save(object);
|
||||
} catch (final FileNotFoundException ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, file.toString(), ex);
|
||||
} finally {
|
||||
onFinish();
|
||||
if (pw != null) {
|
||||
pw.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public abstract void onFinish();
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
package com.earth2me.essentials.storage;
|
||||
|
||||
import com.earth2me.essentials.IConf;
|
||||
import net.ess3.api.IEssentials;
|
||||
import net.ess3.api.IReload;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public abstract class AsyncStorageObjectHolder<T extends StorageObject> implements IConf, IStorageObjectHolder<T>, IReload {
|
||||
protected final transient IEssentials ess;
|
||||
private final transient ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
|
||||
private final transient Class<T> clazz;
|
||||
private transient T data;
|
||||
|
||||
public AsyncStorageObjectHolder(final IEssentials ess, final Class<T> clazz) {
|
||||
this.ess = ess;
|
||||
this.clazz = clazz;
|
||||
try {
|
||||
this.data = clazz.newInstance();
|
||||
} catch (final IllegalAccessException | InstantiationException ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acquireReadLock() {
|
||||
rwl.readLock().lock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acquireWriteLock() {
|
||||
while (rwl.getReadHoldCount() > 0) {
|
||||
rwl.readLock().unlock();
|
||||
}
|
||||
rwl.writeLock().lock();
|
||||
rwl.readLock().lock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
unlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlock() {
|
||||
if (rwl.isWriteLockedByCurrentThread()) {
|
||||
rwl.writeLock().unlock();
|
||||
new StorageObjectDataWriter();
|
||||
}
|
||||
while (rwl.getReadHoldCount() > 0) {
|
||||
rwl.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadConfig() {
|
||||
new StorageObjectDataReader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReload() {
|
||||
new StorageObjectDataReader();
|
||||
}
|
||||
|
||||
public abstract void finishRead();
|
||||
|
||||
public abstract void finishWrite();
|
||||
|
||||
public abstract File getStorageFile();
|
||||
|
||||
private class StorageObjectDataWriter extends AbstractDelayedYamlFileWriter {
|
||||
StorageObjectDataWriter() {
|
||||
super(ess, getStorageFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageObject getObject() {
|
||||
acquireReadLock();
|
||||
return getData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
unlock();
|
||||
finishWrite();
|
||||
}
|
||||
}
|
||||
|
||||
private class StorageObjectDataReader extends AbstractDelayedYamlFileReader<T> {
|
||||
StorageObjectDataReader() {
|
||||
super(ess, getStorageFile(), clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
rwl.writeLock().lock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(final T object) {
|
||||
if (object != null) {
|
||||
data = object;
|
||||
}
|
||||
rwl.writeLock().unlock();
|
||||
finishRead();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onException() {
|
||||
if (data == null) {
|
||||
try {
|
||||
data = clazz.newInstance();
|
||||
} catch (final IllegalAccessException | InstantiationException ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
rwl.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
package com.earth2me.essentials.storage;
|
||||
|
||||
import com.earth2me.essentials.Enchantments;
|
||||
import com.earth2me.essentials.utils.NumberUtil;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.material.MaterialData;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.yaml.snakeyaml.constructor.BaseConstructor;
|
||||
import org.yaml.snakeyaml.constructor.CustomClassLoaderConstructor;
|
||||
import org.yaml.snakeyaml.introspector.PropertyUtils;
|
||||
import org.yaml.snakeyaml.nodes.MappingNode;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
import org.yaml.snakeyaml.nodes.NodeId;
|
||||
import org.yaml.snakeyaml.nodes.NodeTuple;
|
||||
import org.yaml.snakeyaml.nodes.ScalarNode;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class BukkitConstructor extends CustomClassLoaderConstructor {
|
||||
private Method constructScalarMethod = null;
|
||||
|
||||
public BukkitConstructor(final Class<?> clazz, final Plugin plugin) {
|
||||
super(clazz, plugin.getClass().getClassLoader());
|
||||
yamlClassConstructors.put(NodeId.scalar, new ConstructBukkitScalar());
|
||||
yamlClassConstructors.put(NodeId.mapping, new ConstructBukkitMapping());
|
||||
|
||||
final PropertyUtils propertyUtils = getPropertyUtils();
|
||||
propertyUtils.setSkipMissingProperties(true);
|
||||
setPropertyUtils(propertyUtils);
|
||||
}
|
||||
|
||||
protected String constructScalarRefl(final ScalarNode scalarNode) {
|
||||
try {
|
||||
if (constructScalarMethod == null) {
|
||||
constructScalarMethod = BaseConstructor.class.getDeclaredMethod("constructScalar", ScalarNode.class);
|
||||
}
|
||||
return (String) constructScalarMethod.invoke(this, scalarNode);
|
||||
} catch (final NoSuchMethodException
|
||||
| SecurityException
|
||||
| IllegalAccessException
|
||||
| IllegalArgumentException
|
||||
| InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private class ConstructBukkitScalar extends ConstructScalar {
|
||||
|
||||
@Override
|
||||
public Object construct(final Node node) {
|
||||
if (node.getType().equals(Material.class)) {
|
||||
final String val = constructScalarRefl((ScalarNode) node);
|
||||
return Material.matchMaterial(val);
|
||||
}
|
||||
|
||||
if (node.getType().equals(MaterialData.class)) {
|
||||
final String val = constructScalarRefl((ScalarNode) node);
|
||||
if (val.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final String[] split = val.split("[:+',;.]", 2);
|
||||
if (split.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Material mat = Material.matchMaterial(split[0]);
|
||||
|
||||
if (mat == null) {
|
||||
return null;
|
||||
}
|
||||
byte data = 0;
|
||||
if (split.length == 2 && NumberUtil.isInt(split[1])) {
|
||||
data = Byte.parseByte(split[1]);
|
||||
}
|
||||
return new MaterialData(mat, data);
|
||||
}
|
||||
if (node.getType().equals(ItemStack.class)) {
|
||||
final String val = constructScalarRefl((ScalarNode) node);
|
||||
if (val.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final String[] split1 = val.split("\\W");
|
||||
if (split1.length == 0) {
|
||||
return null;
|
||||
}
|
||||
final String[] split2 = split1[0].split("[:+',;.]", 2);
|
||||
if (split2.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Material mat = Material.matchMaterial(split2[0]);
|
||||
|
||||
if (mat == null) {
|
||||
return null;
|
||||
}
|
||||
short data = 0;
|
||||
if (split2.length == 2 && NumberUtil.isInt(split2[1])) {
|
||||
data = Short.parseShort(split2[1]);
|
||||
}
|
||||
int size = mat.getMaxStackSize();
|
||||
if (split1.length > 1 && NumberUtil.isInt(split1[1])) {
|
||||
size = Integer.parseInt(split1[1]);
|
||||
}
|
||||
final ItemStack stack = new ItemStack(mat, size, data);
|
||||
if (split1.length > 2) {
|
||||
for (int i = 2; i < split1.length; i++) {
|
||||
final String[] split3 = split1[0].split("[:+',;.]", 2);
|
||||
if (split3.length < 1) {
|
||||
continue;
|
||||
}
|
||||
final Enchantment enchantment = Enchantments.getByName(split3[0]);
|
||||
if (enchantment == null) {
|
||||
continue;
|
||||
}
|
||||
int level = enchantment.getStartLevel();
|
||||
if (split3.length == 2 && NumberUtil.isInt(split3[1])) {
|
||||
level = Integer.parseInt(split3[1]);
|
||||
}
|
||||
if (level < enchantment.getStartLevel()) {
|
||||
level = enchantment.getStartLevel();
|
||||
}
|
||||
if (level > enchantment.getMaxLevel()) {
|
||||
level = enchantment.getMaxLevel();
|
||||
}
|
||||
stack.addUnsafeEnchantment(enchantment, level);
|
||||
}
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
if (node.getType().equals(EnchantmentLevel.class)) {
|
||||
final String val = constructScalarRefl((ScalarNode) node);
|
||||
if (val.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final String[] split = val.split("[:+',;.]", 2);
|
||||
if (split.length == 0) {
|
||||
return null;
|
||||
}
|
||||
final Enchantment enchant = Enchantments.getByName(split[0]);
|
||||
if (enchant == null) {
|
||||
return null;
|
||||
}
|
||||
int level = enchant.getStartLevel();
|
||||
if (split.length == 2 && NumberUtil.isInt(split[1])) {
|
||||
level = Integer.parseInt(split[1]);
|
||||
}
|
||||
if (level < enchant.getStartLevel()) {
|
||||
level = enchant.getStartLevel();
|
||||
}
|
||||
if (level > enchant.getMaxLevel()) {
|
||||
level = enchant.getMaxLevel();
|
||||
}
|
||||
return new EnchantmentLevel(enchant, level);
|
||||
}
|
||||
return super.construct(node);
|
||||
}
|
||||
}
|
||||
|
||||
private class ConstructBukkitMapping extends ConstructMapping {
|
||||
|
||||
@Override
|
||||
public Object construct(final Node node) {
|
||||
if (node.getType().equals(Location.class)) {
|
||||
//TODO: NPE checks
|
||||
final MappingNode mnode = (MappingNode) node;
|
||||
String worldName = "";
|
||||
double x = 0, y = 0, z = 0;
|
||||
float yaw = 0, pitch = 0;
|
||||
if (mnode.getValue().size() < 4) {
|
||||
return null;
|
||||
}
|
||||
for (final NodeTuple nodeTuple : mnode.getValue()) {
|
||||
final String key = constructScalarRefl((ScalarNode) nodeTuple.getKeyNode());
|
||||
final ScalarNode snode = (ScalarNode) nodeTuple.getValueNode();
|
||||
if (key.equalsIgnoreCase("world")) {
|
||||
worldName = constructScalarRefl(snode);
|
||||
}
|
||||
if (key.equalsIgnoreCase("x")) {
|
||||
x = Double.parseDouble(constructScalarRefl(snode));
|
||||
}
|
||||
if (key.equalsIgnoreCase("y")) {
|
||||
y = Double.parseDouble(constructScalarRefl(snode));
|
||||
}
|
||||
if (key.equalsIgnoreCase("z")) {
|
||||
z = Double.parseDouble(constructScalarRefl(snode));
|
||||
}
|
||||
if (key.equalsIgnoreCase("yaw")) {
|
||||
yaw = Float.parseFloat(constructScalarRefl(snode));
|
||||
}
|
||||
if (key.equalsIgnoreCase("pitch")) {
|
||||
pitch = Float.parseFloat(constructScalarRefl(snode));
|
||||
}
|
||||
}
|
||||
if (worldName == null || worldName.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
final World world = Bukkit.getWorld(worldName);
|
||||
if (world == null) {
|
||||
return null;
|
||||
}
|
||||
return new Location(world, x, y, z, yaw, pitch);
|
||||
}
|
||||
return super.construct(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.earth2me.essentials.storage;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.FIELD)
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Comment {
|
||||
String[] value() default "";
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package com.earth2me.essentials.storage;
|
||||
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class EnchantmentLevel implements Entry<Enchantment, Integer> {
|
||||
private Enchantment enchantment;
|
||||
private int level;
|
||||
|
||||
public EnchantmentLevel(final Enchantment enchantment, final int level) {
|
||||
this.enchantment = enchantment;
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public Enchantment getEnchantment() {
|
||||
return enchantment;
|
||||
}
|
||||
|
||||
public void setEnchantment(final Enchantment enchantment) {
|
||||
this.enchantment = enchantment;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public void setLevel(final int level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enchantment getKey() {
|
||||
return enchantment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer setValue(final Integer v) {
|
||||
final int t = level;
|
||||
level = v;
|
||||
return t;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return enchantment.hashCode() ^ level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (obj instanceof Entry) {
|
||||
final Entry entry = (Entry) obj;
|
||||
if (entry.getKey() instanceof Enchantment && entry.getValue() instanceof Integer) {
|
||||
final Enchantment enchant = (Enchantment) entry.getKey();
|
||||
final Integer lvl = (Integer) entry.getValue();
|
||||
return this.enchantment.equals(enchant) && this.level == lvl;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.earth2me.essentials.storage;
|
||||
|
||||
public interface IStorageObjectHolder<T extends StorageObject> {
|
||||
T getData();
|
||||
|
||||
void acquireReadLock();
|
||||
|
||||
void acquireWriteLock();
|
||||
|
||||
void close();
|
||||
|
||||
void unlock();
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.earth2me.essentials.storage;
|
||||
|
||||
public interface IStorageReader {
|
||||
<T extends StorageObject> T load(final Class<? extends T> clazz) throws ObjectLoadException;
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.earth2me.essentials.storage;
|
||||
|
||||
public interface IStorageWriter {
|
||||
void save(final StorageObject object);
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.earth2me.essentials.storage;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MapKeyType {
|
||||
Class value() default String.class;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.earth2me.essentials.storage;
|
||||
|
||||
public class ObjectLoadException extends Exception {
|
||||
public ObjectLoadException(final Throwable thrwbl) {
|
||||
super(thrwbl);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package com.earth2me.essentials.storage;
|
||||
|
||||
public interface StorageObject {
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package com.earth2me.essentials.storage;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.yaml.snakeyaml.TypeDescription;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.Constructor;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class YamlStorageReader implements IStorageReader {
|
||||
private transient static final Map<Class, Yaml> PREPARED_YAMLS = Collections.synchronizedMap(new HashMap<>());
|
||||
private transient static final Map<Class, ReentrantLock> LOCKS = new HashMap<>();
|
||||
private transient final Reader reader;
|
||||
private transient final Plugin plugin;
|
||||
|
||||
public YamlStorageReader(final Reader reader, final Plugin plugin) {
|
||||
this.reader = reader;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends StorageObject> T load(final Class<? extends T> clazz) throws ObjectLoadException {
|
||||
Yaml yaml = PREPARED_YAMLS.get(clazz);
|
||||
if (yaml == null) {
|
||||
yaml = new Yaml(prepareConstructor(clazz));
|
||||
PREPARED_YAMLS.put(clazz, yaml);
|
||||
}
|
||||
ReentrantLock lock;
|
||||
synchronized (LOCKS) {
|
||||
lock = LOCKS.get(clazz);
|
||||
if (lock == null) {
|
||||
lock = new ReentrantLock();
|
||||
}
|
||||
}
|
||||
lock.lock();
|
||||
try {
|
||||
T object = yaml.load(reader);
|
||||
if (object == null) {
|
||||
object = clazz.newInstance();
|
||||
}
|
||||
return object;
|
||||
} catch (final IllegalAccessException | InstantiationException ex) {
|
||||
throw new ObjectLoadException(ex);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private Constructor prepareConstructor(final Class<?> clazz) {
|
||||
final Constructor constructor = new BukkitConstructor(clazz, plugin);
|
||||
final Set<Class> classes = new HashSet<>();
|
||||
|
||||
prepareConstructor(constructor, classes, clazz);
|
||||
return constructor;
|
||||
}
|
||||
|
||||
private void prepareConstructor(final Constructor constructor, final Set<Class> classes, final Class clazz) {
|
||||
classes.add(clazz);
|
||||
final TypeDescription description = new TypeDescription(clazz);
|
||||
for (final Field field : clazz.getDeclaredFields()) {
|
||||
prepareList(field, description, classes, constructor);
|
||||
prepareMap(field, description, classes, constructor);
|
||||
if (StorageObject.class.isAssignableFrom(field.getType()) && !classes.contains(field.getType())) {
|
||||
prepareConstructor(constructor, classes, field.getType());
|
||||
}
|
||||
}
|
||||
constructor.addTypeDescription(description);
|
||||
}
|
||||
|
||||
private void prepareList(final Field field, final TypeDescription description, final Set<Class> classes, final Constructor constructor) {
|
||||
final ListType listType = field.getAnnotation(ListType.class);
|
||||
if (listType != null) {
|
||||
description.putListPropertyType(field.getName(), listType.value());
|
||||
if (StorageObject.class.isAssignableFrom(listType.value()) && !classes.contains(listType.value())) {
|
||||
prepareConstructor(constructor, classes, listType.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareMap(final Field field, final TypeDescription description, final Set<Class> classes, final Constructor constructor) {
|
||||
final MapValueType mapType = field.getAnnotation(MapValueType.class);
|
||||
if (mapType != null) {
|
||||
final MapKeyType mapKeyType = field.getAnnotation(MapKeyType.class);
|
||||
description.putMapPropertyType(field.getName(), mapKeyType == null ? String.class : mapKeyType.value(), mapType.value());
|
||||
if (StorageObject.class.isAssignableFrom(mapType.value()) && !classes.contains(mapType.value())) {
|
||||
prepareConstructor(constructor, classes, mapType.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,254 +0,0 @@
|
||||
package com.earth2me.essentials.storage;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.material.MaterialData;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class YamlStorageWriter implements IStorageWriter {
|
||||
private transient static final Pattern NON_WORD_PATTERN = Pattern.compile("\\W");
|
||||
private transient static final Yaml YAML = new Yaml();
|
||||
private transient final PrintWriter writer;
|
||||
|
||||
public YamlStorageWriter(final PrintWriter writer) {
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(final StorageObject object) {
|
||||
try {
|
||||
writeToFile(object, 0, object.getClass());
|
||||
} catch (final IllegalArgumentException | IllegalAccessException ex) {
|
||||
Logger.getLogger(YamlStorageWriter.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeToFile(final Object object, final int depth, final Class clazz) throws IllegalAccessException {
|
||||
for (final Field field : clazz.getDeclaredFields()) {
|
||||
final int modifier = field.getModifiers();
|
||||
if (Modifier.isPrivate(modifier) && !Modifier.isTransient(modifier) && !Modifier.isStatic(modifier)) {
|
||||
field.setAccessible(true);
|
||||
|
||||
final Object data = field.get(object);
|
||||
if (writeKey(field, depth, data)) {
|
||||
continue;
|
||||
}
|
||||
if (data instanceof StorageObject) {
|
||||
writer.println();
|
||||
writeToFile(data, depth + 1, data.getClass());
|
||||
} else if (data instanceof Map) {
|
||||
writeMap((Map<Object, Object>) data, depth + 1);
|
||||
} else if (data instanceof Collection) {
|
||||
writeCollection((Collection<Object>) data, depth + 1);
|
||||
} else if (data instanceof Location) {
|
||||
writeLocation((Location) data, depth + 1);
|
||||
} else {
|
||||
writeScalar(data);
|
||||
writer.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean writeKey(final Field field, final int depth, final Object data) {
|
||||
final boolean commentPresent = writeComment(field, depth);
|
||||
if (data == null && !commentPresent) {
|
||||
return true;
|
||||
}
|
||||
writeIndention(depth);
|
||||
if (data == null && commentPresent) {
|
||||
writer.print('#');
|
||||
}
|
||||
final String name = field.getName();
|
||||
writer.print(name);
|
||||
writer.print(": ");
|
||||
if (data == null && commentPresent) {
|
||||
writer.println();
|
||||
writer.println();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean writeComment(final Field field, final int depth) {
|
||||
final boolean commentPresent = field.isAnnotationPresent(Comment.class);
|
||||
if (commentPresent) {
|
||||
final Comment comments = field.getAnnotation(Comment.class);
|
||||
for (final String comment : comments.value()) {
|
||||
final String trimmed = comment.trim();
|
||||
if (trimmed.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
writeIndention(depth);
|
||||
writer.print("# ");
|
||||
writer.print(trimmed);
|
||||
writer.println();
|
||||
}
|
||||
}
|
||||
return commentPresent;
|
||||
}
|
||||
|
||||
private void writeCollection(final Collection<Object> data, final int depth) throws IllegalAccessException {
|
||||
writer.println();
|
||||
if (data.isEmpty()) {
|
||||
writer.println();
|
||||
}
|
||||
for (final Object entry : data) {
|
||||
if (entry != null) {
|
||||
writeIndention(depth);
|
||||
writer.print("- ");
|
||||
if (entry instanceof StorageObject) {
|
||||
writer.println();
|
||||
writeToFile(entry, depth + 1, entry.getClass());
|
||||
} else if (entry instanceof Location) {
|
||||
writeLocation((Location) entry, depth + 1);
|
||||
} else {
|
||||
writeScalar(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
writer.println();
|
||||
}
|
||||
|
||||
private void writeMap(final Map<Object, Object> data, final int depth) throws IllegalArgumentException, IllegalAccessException {
|
||||
writer.println();
|
||||
if (data.isEmpty()) {
|
||||
writer.println();
|
||||
}
|
||||
for (final Entry<Object, Object> entry : data.entrySet()) {
|
||||
final Object value = entry.getValue();
|
||||
if (value != null) {
|
||||
writeIndention(depth);
|
||||
writeKey(entry.getKey());
|
||||
writer.print(": ");
|
||||
if (value instanceof StorageObject) {
|
||||
writer.println();
|
||||
writeToFile(value, depth + 1, value.getClass());
|
||||
} else if (value instanceof Collection) {
|
||||
writeCollection((Collection<Object>) value, depth + 1);
|
||||
} else if (value instanceof Location) {
|
||||
writeLocation((Location) value, depth + 1);
|
||||
} else {
|
||||
writeScalar(value);
|
||||
writer.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeIndention(final int depth) {
|
||||
for (int i = 0; i < depth; i++) {
|
||||
writer.print(" ");
|
||||
}
|
||||
}
|
||||
|
||||
private void writeScalar(final Object data) {
|
||||
if (data instanceof String || data instanceof Boolean || data instanceof Number) {
|
||||
synchronized (YAML) {
|
||||
YAML.dumpAll(Collections.singletonList(data).iterator(), writer);
|
||||
}
|
||||
} else if (data instanceof Material) {
|
||||
writeMaterial(data);
|
||||
writer.println();
|
||||
} else if (data instanceof MaterialData) {
|
||||
writeMaterialData(data);
|
||||
writer.println();
|
||||
} else if (data instanceof ItemStack) {
|
||||
writeItemStack(data);
|
||||
writer.println();
|
||||
} else if (data instanceof EnchantmentLevel) {
|
||||
writeEnchantmentLevel(data);
|
||||
writer.println();
|
||||
} else {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeKey(final Object data) {
|
||||
if (data instanceof String || data instanceof Boolean || data instanceof Number) {
|
||||
final String output = data.toString();
|
||||
if (NON_WORD_PATTERN.matcher(output).find()) {
|
||||
writer.print('"');
|
||||
writer.print(output.replace("\"", "\\\""));
|
||||
writer.print('"');
|
||||
} else {
|
||||
writer.print(output);
|
||||
}
|
||||
} else if (data instanceof Material) {
|
||||
writeMaterial(data);
|
||||
} else if (data instanceof MaterialData) {
|
||||
writeMaterialData(data);
|
||||
} else if (data instanceof EnchantmentLevel) {
|
||||
writeEnchantmentLevel(data);
|
||||
} else {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeMaterial(final Object data) {
|
||||
writer.print(data.toString().toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
|
||||
private void writeMaterialData(final Object data) {
|
||||
final MaterialData matData = (MaterialData) data;
|
||||
writeMaterial(matData.getItemType());
|
||||
if (matData.getData() > 0) {
|
||||
writer.print(':');
|
||||
writer.print(matData.getData());
|
||||
}
|
||||
}
|
||||
|
||||
private void writeItemStack(final Object data) {
|
||||
final ItemStack itemStack = (ItemStack) data;
|
||||
writeMaterialData(itemStack.getData());
|
||||
writer.print(' ');
|
||||
writer.print(itemStack.getAmount());
|
||||
for (final Entry<Enchantment, Integer> entry : itemStack.getEnchantments().entrySet()) {
|
||||
writer.print(' ');
|
||||
writeEnchantmentLevel(entry);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeEnchantmentLevel(final Object data) {
|
||||
final Entry<Enchantment, Integer> enchLevel = (Entry<Enchantment, Integer>) data;
|
||||
writer.print(enchLevel.getKey().getName().toLowerCase(Locale.ENGLISH));
|
||||
writer.print(':');
|
||||
writer.print(enchLevel.getValue());
|
||||
}
|
||||
|
||||
private void writeLocation(final Location entry, final int depth) {
|
||||
writer.println();
|
||||
writeIndention(depth);
|
||||
writer.print("world: ");
|
||||
writeScalar(entry.getWorld().getName());
|
||||
writeIndention(depth);
|
||||
writer.print("x: ");
|
||||
writeScalar(entry.getX());
|
||||
writeIndention(depth);
|
||||
writer.print("y: ");
|
||||
writeScalar(entry.getY());
|
||||
writeIndention(depth);
|
||||
writer.print("z: ");
|
||||
writeScalar(entry.getZ());
|
||||
writeIndention(depth);
|
||||
writer.print("yaw: ");
|
||||
writeScalar(entry.getYaw());
|
||||
writeIndention(depth);
|
||||
writer.print("pitch: ");
|
||||
writeScalar(entry.getPitch());
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.earth2me.essentials.textreader;
|
||||
|
||||
import com.earth2me.essentials.CommandSource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SimpleTextPager {
|
||||
private final transient IText text;
|
||||
|
||||
public SimpleTextPager(final IText text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public void showPage(final CommandSource sender) {
|
||||
for (final String line : text.getLines()) {
|
||||
sender.sendMessage(line);
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getLines() {
|
||||
return text.getLines();
|
||||
}
|
||||
|
||||
public String getLine(final int line) {
|
||||
if (text.getLines().size() < line) {
|
||||
return null;
|
||||
}
|
||||
return text.getLines().get(line);
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
package com.earth2me.essentials.utils;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.DyeColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.material.MaterialData;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
public final class MaterialUtil {
|
||||
|
||||
public static final Material SPAWNER = EnumUtil.getMaterial("MOB_SPAWNER", "SPAWNER");
|
||||
private static final Set<Material> BEDS;
|
||||
private static final Set<Material> BANNERS;
|
||||
private static final Set<Material> FIREWORKS;
|
||||
private static final Set<Material> LEGACY_SKULLS;
|
||||
private static final Set<Material> LEATHER_ARMOR;
|
||||
private static final Set<Material> MOB_HEADS;
|
||||
// includes TIPPED_ARROW which also has potion effects
|
||||
private static final Set<Material> PLAYER_HEADS;
|
||||
private static final Set<Material> POTIONS;
|
||||
private static final Set<Material> SIGN_POSTS;
|
||||
private static final Set<Material> WALL_SIGNS;
|
||||
|
||||
static {
|
||||
|
||||
BEDS = EnumUtil.getAllMatching(Material.class, "BED", "BED_BLOCK", "WHITE_BED", "ORANGE_BED",
|
||||
"MAGENTA_BED", "LIGHT_BLUE_BED", "YELLOW_BED", "LIME_BED", "PINK_BED", "GRAY_BED",
|
||||
"LIGHT_GRAY_BED", "CYAN_BED", "PURPLE_BED", "BLUE_BED", "BROWN_BED", "GREEN_BED",
|
||||
"RED_BED", "BLACK_BED");
|
||||
|
||||
BANNERS = EnumUtil.getAllMatching(Material.class, "BANNER", "WHITE_BANNER",
|
||||
"ORANGE_BANNER", "MAGENTA_BANNER", "LIGHT_BLUE_BANNER", "YELLOW_BANNER", "LIME_BANNER",
|
||||
"PINK_BANNER", "GRAY_BANNER", "LIGHT_GRAY_BANNER", "CYAN_BANNER", "PURPLE_BANNER",
|
||||
"BLUE_BANNER", "BROWN_BANNER", "GREEN_BANNER", "RED_BANNER", "BLACK_BANNER", "SHIELD");
|
||||
|
||||
FIREWORKS = EnumUtil.getAllMatching(Material.class, "FIREWORK", "FIREWORK_ROCKET",
|
||||
"FIREWORK_CHARGE", "FIREWORK_STAR");
|
||||
|
||||
LEATHER_ARMOR = EnumUtil.getAllMatching(Material.class, "LEATHER_HELMET",
|
||||
"LEATHER_CHESTPLATE", "LEATHER_LEGGINGS", "LEATHER_BOOTS");
|
||||
|
||||
LEGACY_SKULLS = EnumUtil.getAllMatching(Material.class, "SKULL", "SKULL_ITEM");
|
||||
|
||||
MOB_HEADS = EnumUtil.getAllMatching(Material.class, "SKELETON_SKULL",
|
||||
"SKELETON_WALL_SKULL", "WITHER_SKELETON_SKULL", "WITHER_SKELETON_WALL_SKULL",
|
||||
"CREEPER_HEAD", "CREEPER_WALL_HEAD", "ZOMBIE_HEAD", "ZOMBIE_WALL_HEAD", "DRAGON_HEAD"
|
||||
, "DRAGON_WALL_HEAD");
|
||||
|
||||
PLAYER_HEADS = EnumUtil.getAllMatching(Material.class, "PLAYER_HEAD", "PLAYER_WALL_HEAD");
|
||||
|
||||
POTIONS = EnumUtil.getAllMatching(Material.class, "POTION", "SPLASH_POTION",
|
||||
"LINGERING_POTION", "TIPPED_ARROW");
|
||||
|
||||
SIGN_POSTS = EnumUtil.getAllMatching(Material.class, "SIGN", "SIGN_POST",
|
||||
"ACACIA_SIGN", "BIRCH_SIGN",
|
||||
"DARK_OAK_SIGN", "JUNGLE_SIGN",
|
||||
"OAK_SIGN", "SPRUCE_SIGN",
|
||||
"CRIMSON_SIGN", "WARPED_SIGN");
|
||||
|
||||
WALL_SIGNS = EnumUtil.getAllMatching(Material.class, "WALL_SIGN",
|
||||
"ACACIA_WALL_SIGN", "BIRCH_WALL_SIGN",
|
||||
"DARK_OAK_WALL_SIGN", "JUNGLE_WALL_SIGN",
|
||||
"OAK_WALL_SIGN", "SPRUCE_WALL_SIGN",
|
||||
"CRIMSON_WALL_SIGN", "WARPED_WALL_SIGN");
|
||||
}
|
||||
|
||||
private MaterialUtil() {
|
||||
}
|
||||
|
||||
public static boolean isBed(final Material material) {
|
||||
return BEDS.contains(material);
|
||||
}
|
||||
|
||||
public static boolean isBanner(final Material material) {
|
||||
return BANNERS.contains(material);
|
||||
}
|
||||
|
||||
public static boolean isFirework(final Material material) {
|
||||
return FIREWORKS.contains(material);
|
||||
}
|
||||
|
||||
public static boolean isLeatherArmor(final Material material) {
|
||||
return LEATHER_ARMOR.contains(material);
|
||||
}
|
||||
|
||||
public static boolean isMobHead(final Material material, final int durability) {
|
||||
if (MOB_HEADS.contains(material)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return LEGACY_SKULLS.contains(material) && (durability != 3);
|
||||
}
|
||||
|
||||
public static boolean isPlayerHead(final Material material, final int durability) {
|
||||
if (PLAYER_HEADS.contains(material)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return LEGACY_SKULLS.contains(material) && durability == 3;
|
||||
}
|
||||
|
||||
public static boolean isPotion(final Material material) {
|
||||
return POTIONS.contains(material);
|
||||
}
|
||||
|
||||
public static boolean isSignPost(final Material material) {
|
||||
return SIGN_POSTS.contains(material);
|
||||
}
|
||||
|
||||
public static boolean isWallSign(final Material material) {
|
||||
return WALL_SIGNS.contains(material);
|
||||
}
|
||||
|
||||
public static boolean isSign(final Material material) {
|
||||
return isSignPost(material) || isWallSign(material);
|
||||
}
|
||||
|
||||
public static boolean isSkull(final Material material) {
|
||||
return isPlayerHead(material, -1) || isMobHead(material, -1);
|
||||
}
|
||||
|
||||
public static Material convertFromLegacy(final int id, final byte damage) {
|
||||
for (final Material material : EnumSet.allOf(Material.class)) {
|
||||
if (material.getId() == id) {
|
||||
try {
|
||||
return Bukkit.getUnsafe().fromLegacy(new MaterialData(material, damage));
|
||||
} catch (final NoSuchMethodError error) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
//noinspection JavaReflectionMemberAccess
|
||||
final Method getMaterialFromId = Material.class.getDeclaredMethod("getMaterial", int.class);
|
||||
return (Material) getMaterialFromId.invoke(null, id);
|
||||
} catch (final NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static DyeColor getColorOf(final Material material) {
|
||||
for (final DyeColor color : DyeColor.values()) {
|
||||
if (material.toString().contains(color.name())) {
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
||||
return DyeColor.WHITE;
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
package com.earth2me.essentials.utils;
|
||||
|
||||
import net.ess3.api.IEssentials;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public final class NumberUtil {
|
||||
|
||||
private static final DecimalFormat twoDPlaces = new DecimalFormat("#,###.##");
|
||||
private static final DecimalFormat currencyFormat = new DecimalFormat("#0.00", DecimalFormatSymbols.getInstance(Locale.US));
|
||||
|
||||
// This field is likely to be modified in com.earth2me.essentials.Settings when loading currency format.
|
||||
// This ensures that we can supply a constant formatting.
|
||||
private static NumberFormat PRETTY_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
|
||||
static {
|
||||
twoDPlaces.setRoundingMode(RoundingMode.HALF_UP);
|
||||
currencyFormat.setRoundingMode(RoundingMode.FLOOR);
|
||||
|
||||
PRETTY_FORMAT.setRoundingMode(RoundingMode.FLOOR);
|
||||
PRETTY_FORMAT.setGroupingUsed(true);
|
||||
PRETTY_FORMAT.setMinimumFractionDigits(2);
|
||||
PRETTY_FORMAT.setMaximumFractionDigits(2);
|
||||
}
|
||||
|
||||
private NumberUtil() {
|
||||
}
|
||||
|
||||
// this method should only be called by Essentials
|
||||
public static void internalSetPrettyFormat(final NumberFormat prettyFormat) {
|
||||
PRETTY_FORMAT = prettyFormat;
|
||||
}
|
||||
|
||||
public static String shortCurrency(final BigDecimal value, final IEssentials ess) {
|
||||
if (ess.getSettings().isCurrencySymbolSuffixed()) {
|
||||
return formatAsCurrency(value) + ess.getSettings().getCurrencySymbol();
|
||||
}
|
||||
return ess.getSettings().getCurrencySymbol() + formatAsCurrency(value);
|
||||
}
|
||||
|
||||
public static String formatDouble(final double value) {
|
||||
return twoDPlaces.format(value);
|
||||
}
|
||||
|
||||
public static String formatAsCurrency(final BigDecimal value) {
|
||||
String str = currencyFormat.format(value);
|
||||
if (str.endsWith(".00")) {
|
||||
str = str.substring(0, str.length() - 3);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public static String formatAsPrettyCurrency(final BigDecimal value) {
|
||||
String str = PRETTY_FORMAT.format(value);
|
||||
if (str.endsWith(".00")) {
|
||||
str = str.substring(0, str.length() - 3);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public static String displayCurrency(final BigDecimal value, final IEssentials ess) {
|
||||
String currency = formatAsPrettyCurrency(value);
|
||||
String sign = "";
|
||||
if (value.signum() < 0) {
|
||||
currency = currency.substring(1);
|
||||
sign = "-";
|
||||
}
|
||||
if (ess.getSettings().isCurrencySymbolSuffixed()) {
|
||||
return sign + tl("currency", currency, ess.getSettings().getCurrencySymbol());
|
||||
}
|
||||
return sign + tl("currency", ess.getSettings().getCurrencySymbol(), currency);
|
||||
}
|
||||
|
||||
public static String displayCurrencyExactly(final BigDecimal value, final IEssentials ess) {
|
||||
String currency = value.toPlainString();
|
||||
String sign = "";
|
||||
if (value.signum() < 0) {
|
||||
currency = currency.substring(1);
|
||||
sign = "-";
|
||||
}
|
||||
if (ess.getSettings().isCurrencySymbolSuffixed()) {
|
||||
return sign + tl("currency", currency, ess.getSettings().getCurrencySymbol());
|
||||
}
|
||||
return sign + tl("currency", ess.getSettings().getCurrencySymbol(), currency);
|
||||
}
|
||||
|
||||
public static String sanitizeCurrencyString(final String input, final IEssentials ess) {
|
||||
final String symbol = ess.getSettings().getCurrencySymbol();
|
||||
final boolean suffix = ess.getSettings().isCurrencySymbolSuffixed();
|
||||
if (input.contains(symbol)) {
|
||||
return suffix ? input.substring(0, input.indexOf(symbol)) : input.substring(symbol.length());
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
public static boolean isInt(final String sInt) {
|
||||
try {
|
||||
Integer.parseInt(sInt);
|
||||
} catch (final NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isPositiveInt(final String sInt) {
|
||||
if (!isInt(sInt)) {
|
||||
return false;
|
||||
}
|
||||
return Integer.parseInt(sInt) > 0;
|
||||
}
|
||||
}
|
||||
@@ -1,224 +0,0 @@
|
||||
package com.earth2me.essentials.utils;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class VersionUtil {
|
||||
|
||||
public static final BukkitVersion v1_14_R01 = BukkitVersion.fromString("1.14-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_8_8_R01 = BukkitVersion.fromString("1.8.8-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_9_R01 = BukkitVersion.fromString("1.9-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_9_4_R01 = BukkitVersion.fromString("1.9.4-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_10_R01 = BukkitVersion.fromString("1.10-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_10_2_R01 = BukkitVersion.fromString("1.10.2-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_11_R01 = BukkitVersion.fromString("1.11-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_11_2_R01 = BukkitVersion.fromString("1.11.2-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_12_0_R01 = BukkitVersion.fromString("1.12.0-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_12_2_R01 = BukkitVersion.fromString("1.12.2-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_13_0_R01 = BukkitVersion.fromString("1.13.0-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_13_2_R01 = BukkitVersion.fromString("1.13.2-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_14_4_R01 = BukkitVersion.fromString("1.14.4-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_15_R01 = BukkitVersion.fromString("1.15-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_15_2_R01 = BukkitVersion.fromString("1.15.2-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_16_1_R01 = BukkitVersion.fromString("1.16.1-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_16_2_R01 = BukkitVersion.fromString("1.16.2-R0.1-SNAPSHOT");
|
||||
public static final BukkitVersion v1_16_4_R01 = BukkitVersion.fromString("1.16.4-R0.1-SNAPSHOT");
|
||||
|
||||
private static final Set<BukkitVersion> supportedVersions = ImmutableSet.of(v1_8_8_R01, v1_9_4_R01, v1_10_2_R01, v1_11_2_R01, v1_12_2_R01, v1_13_2_R01, v1_14_4_R01, v1_15_2_R01, v1_16_4_R01);
|
||||
|
||||
private static BukkitVersion serverVersion = null;
|
||||
|
||||
private VersionUtil() {
|
||||
}
|
||||
|
||||
public static BukkitVersion getServerBukkitVersion() {
|
||||
if (serverVersion == null) {
|
||||
serverVersion = BukkitVersion.fromString(Bukkit.getServer().getBukkitVersion());
|
||||
}
|
||||
return serverVersion;
|
||||
}
|
||||
|
||||
public static SupportStatus getServerSupportStatus() {
|
||||
try {
|
||||
Class.forName("net.minecraftforge.common.MinecraftForge");
|
||||
return SupportStatus.UNSTABLE;
|
||||
} catch (final ClassNotFoundException ignored) {
|
||||
}
|
||||
|
||||
if (!supportedVersions.contains(getServerBukkitVersion())) {
|
||||
return SupportStatus.OUTDATED;
|
||||
}
|
||||
|
||||
return PaperLib.isPaper() ? SupportStatus.FULL : SupportStatus.LIMITED;
|
||||
}
|
||||
|
||||
public static boolean isServerSupported() {
|
||||
return getServerSupportStatus().isSupported();
|
||||
}
|
||||
|
||||
public static final class BukkitVersion implements Comparable<BukkitVersion> {
|
||||
private static final Pattern VERSION_PATTERN = Pattern.compile("^(\\d+)\\.(\\d+)\\.?([0-9]*)?(?:-pre(\\d))?(?:-?R?([\\d.]+))?(?:-SNAPSHOT)?");
|
||||
|
||||
private final int major;
|
||||
private final int minor;
|
||||
private final int prerelease;
|
||||
private final int patch;
|
||||
private final double revision;
|
||||
|
||||
private BukkitVersion(final int major, final int minor, final int patch, final double revision, final int prerelease) {
|
||||
this.major = major;
|
||||
this.minor = minor;
|
||||
this.patch = patch;
|
||||
this.revision = revision;
|
||||
this.prerelease = prerelease;
|
||||
}
|
||||
|
||||
public static BukkitVersion fromString(final String string) {
|
||||
Preconditions.checkNotNull(string, "string cannot be null.");
|
||||
Matcher matcher = VERSION_PATTERN.matcher(string);
|
||||
if (!matcher.matches()) {
|
||||
if (!Bukkit.getName().equals("Essentials Fake Server")) {
|
||||
throw new IllegalArgumentException(string + " is not in valid version format. e.g. 1.8.8-R0.1");
|
||||
}
|
||||
matcher = VERSION_PATTERN.matcher(v1_14_R01.toString());
|
||||
Preconditions.checkArgument(matcher.matches(), string + " is not in valid version format. e.g. 1.8.8-R0.1");
|
||||
}
|
||||
|
||||
return from(matcher.group(1), matcher.group(2), matcher.group(3), matcher.groupCount() < 5 ? "" : matcher.group(5), matcher.group(4));
|
||||
}
|
||||
|
||||
private static BukkitVersion from(final String major, final String minor, String patch, String revision, String prerelease) {
|
||||
if (patch == null || patch.isEmpty()) patch = "0";
|
||||
if (revision == null || revision.isEmpty()) revision = "0";
|
||||
if (prerelease == null || prerelease.isEmpty()) prerelease = "-1";
|
||||
return new BukkitVersion(Integer.parseInt(major),
|
||||
Integer.parseInt(minor),
|
||||
Integer.parseInt(patch),
|
||||
Double.parseDouble(revision),
|
||||
Integer.parseInt(prerelease));
|
||||
}
|
||||
|
||||
public boolean isHigherThan(final BukkitVersion o) {
|
||||
return compareTo(o) > 0;
|
||||
}
|
||||
|
||||
public boolean isHigherThanOrEqualTo(final BukkitVersion o) {
|
||||
return compareTo(o) >= 0;
|
||||
}
|
||||
|
||||
public boolean isLowerThan(final BukkitVersion o) {
|
||||
return compareTo(o) < 0;
|
||||
}
|
||||
|
||||
public boolean isLowerThanOrEqualTo(final BukkitVersion o) {
|
||||
return compareTo(o) <= 0;
|
||||
}
|
||||
|
||||
public int getMajor() {
|
||||
return major;
|
||||
}
|
||||
|
||||
public int getMinor() {
|
||||
return minor;
|
||||
}
|
||||
|
||||
public int getPatch() {
|
||||
return patch;
|
||||
}
|
||||
|
||||
public double getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
public int getPrerelease() {
|
||||
return prerelease;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final BukkitVersion that = (BukkitVersion) o;
|
||||
return major == that.major &&
|
||||
minor == that.minor &&
|
||||
patch == that.patch &&
|
||||
revision == that.revision &&
|
||||
prerelease == that.prerelease;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(major, minor, patch, revision, prerelease);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder(major + "." + minor);
|
||||
if (patch != 0) {
|
||||
sb.append(".").append(patch);
|
||||
}
|
||||
if (prerelease != -1) {
|
||||
sb.append("-pre").append(prerelease);
|
||||
}
|
||||
return sb.append("-R").append(revision).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final BukkitVersion o) {
|
||||
if (major < o.major) {
|
||||
return -1;
|
||||
} else if (major > o.major) {
|
||||
return 1;
|
||||
} else { // equal major
|
||||
if (minor < o.minor) {
|
||||
return -1;
|
||||
} else if (minor > o.minor) {
|
||||
return 1;
|
||||
} else { // equal minor
|
||||
if (patch < o.patch) {
|
||||
return -1;
|
||||
} else if (patch > o.patch) {
|
||||
return 1;
|
||||
} else { // equal patch
|
||||
if (prerelease < o.prerelease) {
|
||||
return -1;
|
||||
} else if (prerelease > o.prerelease) {
|
||||
return 1;
|
||||
} else { // equal prerelease
|
||||
return Double.compare(revision, o.revision);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum SupportStatus {
|
||||
FULL(true),
|
||||
LIMITED(true),
|
||||
UNSTABLE(false),
|
||||
OUTDATED(false)
|
||||
;
|
||||
|
||||
private final boolean supported;
|
||||
|
||||
SupportStatus(final boolean supported) {
|
||||
this.supported = supported;
|
||||
}
|
||||
|
||||
public boolean isSupported() {
|
||||
return supported;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
Loaded 100 of 904 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user