Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6bf44f6208 | ||
|
|
428dd3cd06 | ||
|
|
871e2efbe2 | ||
|
|
a5b45e2c82 | ||
|
|
9feb3dfac6 | ||
|
|
5961d0dca0 | ||
|
|
3d5d4fcab2 | ||
|
|
0763a9b3b4 | ||
|
|
2d67a89fc2 | ||
|
|
4207d875f9 | ||
|
|
5da420c6c6 | ||
|
|
1ffa4842c3 | ||
|
|
819e194f27 | ||
|
|
269e55b93f | ||
|
|
04412f7138 | ||
|
|
0fce9d01e1 | ||
|
|
1c29301635 | ||
|
|
8bd7bbb5d7 | ||
|
|
1a99663b59 | ||
|
|
36e31279b2 | ||
|
|
4aa7eba675 | ||
|
|
3bbe2d26b8 | ||
|
|
782ad838be | ||
|
|
ceddb26137 | ||
|
|
ff044d75c7 | ||
|
|
8955df695d | ||
|
|
8ddcb208f5 | ||
|
|
95834df89f | ||
|
|
2f67f26cbe | ||
|
|
00957f9458 | ||
|
|
2e9e886c48 | ||
|
|
d2d4e48b73 | ||
|
|
fe16df27f2 | ||
|
|
cc29f70dff | ||
|
|
e136f7cfaf | ||
|
|
fd52f124fb | ||
|
|
5a656cee39 | ||
|
|
21a89f9b14 | ||
|
|
9ded4d2d57 | ||
|
|
fb64d92879 | ||
|
|
1a4bcae4d1 | ||
|
|
25ee4da4f4 | ||
|
|
35cb39c35e | ||
|
|
3a5f232f92 | ||
|
|
4ed201fba9 | ||
|
|
03835f71d9 | ||
|
|
480e50e71e | ||
|
|
39110b2898 | ||
|
|
e8db70e319 | ||
|
|
eb907988fb | ||
|
|
95935022b8 | ||
|
|
1e70cb0651 | ||
|
|
1594d062e5 | ||
|
|
04b266f558 | ||
|
|
9989fec809 | ||
|
|
ab8eabc348 | ||
|
|
931b8b5c36 | ||
|
|
64957ba13c | ||
|
|
a03be990ab | ||
|
|
44729495f5 | ||
|
|
9e7500f0cd | ||
|
|
d10ff8bdbc | ||
|
|
f91fc36365 | ||
|
|
84ddff07a2 | ||
|
|
4a89658cf4 | ||
|
|
5daef41031 | ||
|
|
07cc1e8160 | ||
|
|
8b2d763207 | ||
|
|
fb8c556e24 | ||
|
|
397fa12f12 | ||
|
|
d86ecf8d8b | ||
|
|
2882e9ecdf | ||
|
|
69a4a05069 | ||
|
|
361a340ef1 | ||
|
|
ee7c0451af | ||
|
|
200c7cbfea | ||
|
|
9a665ddf26 | ||
|
|
c4327ac238 | ||
|
|
9f4b798848 | ||
|
|
3cc18f4dc0 | ||
|
|
9637308045 | ||
|
|
772603e785 | ||
|
|
f9417ee2f7 | ||
|
|
2db85bff60 | ||
|
|
527b5aa25c | ||
|
|
5d9bb0dc4a | ||
|
|
cc51756f0d | ||
|
|
5ff66adb0c | ||
|
|
8e9cc98ded | ||
|
|
4387e78007 | ||
|
|
4fff1e24cc | ||
|
|
9791ed0cee | ||
|
|
e8e6f3a8fd | ||
|
|
5e6da9b24e | ||
|
|
59bd9239b2 | ||
|
|
08436a278c | ||
|
|
5d512e5e89 | ||
|
|
ec1a367691 | ||
|
|
457450683b | ||
|
|
a8d76d6b0f | ||
|
|
5036d62824 | ||
|
|
4f5f6e724f | ||
|
|
71f7532fe3 | ||
|
|
9ea37af603 | ||
|
|
1964a6b4e3 | ||
|
|
94a9ea31a0 | ||
|
|
2855d268da | ||
|
|
d6d576da20 | ||
|
|
34ed832d79 | ||
|
|
42ef8c0c39 | ||
|
|
1ce0c2db4e | ||
|
|
028aaa1cf7 | ||
|
|
66937916c4 | ||
|
|
82a053095e | ||
|
|
f24e62687d | ||
|
|
184cf8443c | ||
|
|
2b06bcc497 | ||
|
|
743f4cc5eb | ||
|
|
45ccb18133 | ||
|
|
3812e11702 | ||
|
|
c731cd3c87 | ||
|
|
466a24eea3 | ||
|
|
d747a0c19a | ||
|
|
59a64620bf | ||
|
|
f36b4b4078 | ||
|
|
9298b0270b | ||
|
|
d8dc50ceec | ||
|
|
60be698c61 | ||
|
|
0b37e92260 | ||
|
|
b7b1b63c4e | ||
|
|
98b923aa54 | ||
|
|
b24d7cb849 | ||
|
|
7df7157d5f | ||
|
|
47263746db | ||
|
|
ff06b06966 | ||
|
|
fe42d282d1 | ||
|
|
12df4e49c6 | ||
|
|
06812b2725 | ||
|
|
93e4ae7ca7 | ||
|
|
d57f3f20e3 | ||
|
|
f418fb961b | ||
|
|
267af92ceb | ||
|
|
57448ddaf6 | ||
|
|
9f40af4553 | ||
|
|
e25da2c6c5 | ||
|
|
897cccaa86 | ||
|
|
512efd0efe | ||
|
|
b67fa683c6 | ||
|
|
34387925b8 | ||
|
|
303dbc5ae9 | ||
|
|
ee565ff6f7 | ||
|
|
98baaab36a | ||
|
|
92e9d790a4 | ||
|
|
b8f2e2932b | ||
|
|
45f9202bdd | ||
|
|
ffeffc70c3 | ||
|
|
60080a5af6 | ||
|
|
1191265c8c | ||
|
|
27dfeea12c | ||
|
|
e50ec1632d | ||
|
|
a0d4dedc9d | ||
|
|
9eda026738 | ||
|
|
f3b81b7cb6 | ||
|
|
cf766ddd9a | ||
|
|
12cd2f5e6b | ||
|
|
04ef50ea91 | ||
|
|
d4c627fa79 | ||
|
|
deffc28ab6 | ||
|
|
833f8f5dbb | ||
|
|
5bdf2fa301 | ||
|
|
d13607873c | ||
|
|
a1f11dac03 | ||
|
|
74f1805441 | ||
|
|
21046d8eee | ||
|
|
0c37e3bb35 | ||
|
|
3f9ea7035f | ||
|
|
b55c56af12 | ||
|
|
498a948605 | ||
|
|
76a1890bba | ||
|
|
8899770518 | ||
|
|
a7d29d292f | ||
|
|
a9ca388f4e | ||
|
|
c42a88142c | ||
|
|
9fe78d3e6e | ||
|
|
816a58493a | ||
|
|
bc84cd2ec9 | ||
|
|
5f7fda250c | ||
|
|
03b58da260 | ||
|
|
238049b58c | ||
|
|
34a701cbe9 | ||
|
|
bdf42b3ef1 | ||
|
|
7b82fa17d0 | ||
|
|
05f1f285d0 | ||
|
|
12813a4a00 | ||
|
|
176b02ebe3 | ||
|
|
bd53bf7eb2 | ||
|
|
ae17d965e6 | ||
|
|
17b69fd38c | ||
|
|
ee9c6f77ec | ||
|
|
ddeb523154 | ||
|
|
04ec8e1927 | ||
|
|
f3f3cc7927 | ||
|
|
c7dd3f5344 | ||
|
|
19f534845f | ||
|
|
3c3110a3f8 | ||
|
|
2aa551090c | ||
|
|
57861e435c | ||
|
|
205e1e065d | ||
|
|
79ee776167 | ||
|
|
19869084e5 | ||
|
|
f1bd608fca | ||
|
|
2474e390f7 | ||
|
|
ef737d5566 | ||
|
|
9c64d3521e | ||
|
|
c7c8636720 | ||
|
|
cda72b3b3a | ||
|
|
04a1101045 | ||
|
|
44e3e51bbc | ||
|
|
40f2c44b5d | ||
|
|
16c26af04e | ||
|
|
85aa30f82c | ||
|
|
0ad119b6e0 | ||
|
|
a612e2216b | ||
|
|
2c0d3db9f4 | ||
|
|
8de820747a | ||
|
|
6469893b15 | ||
|
|
a46feb5e03 | ||
|
|
2e39683687 | ||
|
|
78464ac222 | ||
|
|
92b12fd612 | ||
|
|
5e90191aa4 | ||
|
|
1b4ee54b73 | ||
|
|
409c007759 | ||
|
|
49cd7410cf | ||
|
|
978b21360e | ||
|
|
0d1fbc15ce | ||
|
|
1cf3ba543d | ||
|
|
358830cbfb | ||
|
|
ea193d7bd3 | ||
|
|
8e59336833 | ||
|
|
5083f0a3d2 | ||
|
|
d20fdd3086 | ||
|
|
e005d80192 | ||
|
|
afd791b808 | ||
|
|
0acdcba09b | ||
|
|
872078ff05 | ||
|
|
4bfb1ec29b | ||
|
|
97f073685e | ||
|
|
08ab52d7f0 | ||
|
|
e7b7c78b11 | ||
|
|
7df02868ad | ||
|
|
063b5f90e9 | ||
|
|
dd700f99e2 | ||
|
|
7b9f418b23 | ||
|
|
fa163e0593 | ||
|
|
8ba86bec71 | ||
|
|
971f404f20 | ||
|
|
f378b1e10a | ||
|
|
13a281ebf7 | ||
|
|
e7f210739f | ||
|
|
addca59c39 | ||
|
|
f93880a0f5 | ||
|
|
489e8b60aa | ||
|
|
bf418d8a9a | ||
|
|
1e1f11cde9 | ||
|
|
34179eb850 | ||
|
|
cfcd488d44 | ||
|
|
a49b838b83 | ||
|
|
fdeec1e97c | ||
|
|
39ba75410e | ||
|
|
c20d6be447 | ||
|
|
d2f7f256d3 | ||
|
|
fb6abf7161 | ||
|
|
445bdac8a7 | ||
|
|
7bdabf93b8 | ||
|
|
8d9c0549b7 | ||
|
|
4d94a4879b | ||
|
|
924a72c537 | ||
|
|
75abdccd06 | ||
|
|
bb4b92e37e | ||
|
|
880616135f | ||
|
|
be72744651 | ||
|
|
c43090bdd8 | ||
|
|
50143c2374 | ||
|
|
951f1853fc | ||
|
|
a69962dd86 | ||
|
|
29e0bf8476 | ||
|
|
ee41a234e8 | ||
|
|
c8cf01319a | ||
|
|
3f4da7884f | ||
|
|
965df44783 | ||
|
|
b9b8f8b36d | ||
|
|
d293cdfb39 | ||
|
|
e3dc3ea548 | ||
|
|
3037d3a742 | ||
|
|
2230800deb | ||
|
|
1f0b8f783f | ||
|
|
da8d4e93ab | ||
|
|
aa38567232 | ||
|
|
b6757b9622 | ||
|
|
ca288fdf75 | ||
|
|
8b74c29bbe | ||
|
|
5aae4a17ef | ||
|
|
778212d48a | ||
|
|
8d126436ea | ||
|
|
ec40db1b26 | ||
|
|
263c197845 | ||
|
|
7bdc14aa30 | ||
|
|
7ba5209222 | ||
|
|
71e62e0bae | ||
|
|
4a8029ec31 | ||
|
|
382038b9a4 | ||
|
|
7f59851a3b | ||
|
|
0ebcc449f8 | ||
|
|
323614d8c5 | ||
|
|
264dedca34 | ||
|
|
ce26decb7d | ||
|
|
37085e82f0 | ||
|
|
041430e842 | ||
|
|
6762cbc245 | ||
|
|
917d3640f0 | ||
|
|
0cece71f37 | ||
|
|
d9c9f30cea | ||
|
|
dcb119df96 | ||
|
|
78ae7fbcb2 | ||
|
|
1545aad32d | ||
|
|
035d485561 | ||
|
|
f34a1ad2e4 | ||
|
|
5ceef81927 | ||
|
|
6315f74312 | ||
|
|
086a45593b | ||
|
|
1095166155 | ||
|
|
bf89a7cb12 | ||
|
|
729b671c5a | ||
|
|
380791cd45 | ||
|
|
ca80083414 | ||
|
|
ee7fa16514 | ||
|
|
c86a3d4101 | ||
|
|
6fd6a8fe38 | ||
|
|
b05b38e0e8 | ||
|
|
d55298d61b | ||
|
|
37ccf7176c | ||
|
|
868c93ac09 | ||
|
|
f927e836f5 | ||
|
|
2172628985 | ||
|
|
628b1763da | ||
|
|
bbe5fea132 | ||
|
|
8b555a8f53 | ||
|
|
a5ca6fb53e | ||
|
|
164a4823e8 | ||
|
|
352191695a | ||
|
|
b2235e1d8b | ||
|
|
324b6d6f82 | ||
|
|
fd6e2e736f | ||
|
|
efd5164b8e | ||
|
|
cdb86ae83f | ||
|
|
90c02da02e | ||
|
|
53d56c5891 | ||
|
|
7dd02fa0e5 | ||
|
|
e02b944181 | ||
|
|
299eff1b0d | ||
|
|
aa24ab336b | ||
|
|
a8b9e69e30 | ||
|
|
065c9abf9c | ||
|
|
bdc9668b29 | ||
|
|
7d1abdd2b2 | ||
|
|
667c642708 | ||
|
|
79b13ceab6 | ||
|
|
169f560b2c | ||
|
|
7e8c41c27a | ||
|
|
973966c844 | ||
|
|
eee90cc02f | ||
|
|
ecf052b955 | ||
|
|
a791c86df0 | ||
|
|
63d0b0ac62 | ||
|
|
c46bc37818 | ||
|
|
268d5b84c9 | ||
|
|
0c63abfac8 | ||
|
|
35ca151424 | ||
|
|
e28800f72f | ||
|
|
8232357aba | ||
|
|
eae7f1117c | ||
|
|
d25053b285 | ||
|
|
57273bd265 | ||
|
|
9cf694a7d6 | ||
|
|
5bc0c7b843 | ||
|
|
1c48fd61bc | ||
|
|
8e622b4038 | ||
|
|
35e37bca2e | ||
|
|
cdb508994f | ||
|
|
1113c41379 | ||
|
|
d186ca82a2 | ||
|
|
5cda9e819b | ||
|
|
fe5a73440b | ||
|
|
a91e839f8c | ||
|
|
d4668e0e76 | ||
|
|
305f3452c4 | ||
|
|
fec3361fd7 | ||
|
|
f0861ffa6a | ||
|
|
cfca3f72dc | ||
|
|
aca83e8350 | ||
|
|
8383c8b97f | ||
|
|
3d1ddaa782 | ||
|
|
1d615e252f | ||
|
|
310e72d048 | ||
|
|
2754bf13aa | ||
|
|
9ff5fcdaa7 | ||
|
|
0ba7ed6ece | ||
|
|
2d8c774401 | ||
|
|
f68be63f64 | ||
|
|
2695b393d8 | ||
|
|
55821d2edf | ||
|
|
b64e32503e | ||
|
|
c5ab9b118f | ||
|
|
6cc54349b5 | ||
|
|
98d2de975e | ||
|
|
6d1c118660 | ||
|
|
49e5b195ec | ||
|
|
c92611e7f8 | ||
|
|
233414c45c | ||
|
|
c0629e10bc | ||
|
|
621eb7c489 | ||
|
|
38e16f1d4b | ||
|
|
2e61531079 | ||
|
|
5cf295d4c5 | ||
|
|
24d5865a57 | ||
|
|
03c5e7d9d0 | ||
|
|
7874fcf265 | ||
|
|
3e9070ebf9 | ||
|
|
478fa88a97 | ||
|
|
346f94decc | ||
|
|
f5e8a7e460 | ||
|
|
3cb470a086 | ||
|
|
348ecce315 | ||
|
|
78463bca4f | ||
|
|
a6bde40f30 | ||
|
|
45fa7b5d69 | ||
|
|
1f52ff39d1 | ||
|
|
bc66d925ff | ||
|
|
41f8f6b29d | ||
|
|
e032b2cc7f | ||
|
|
7c6d4a3840 | ||
|
|
2f2095237d | ||
|
|
2a20af1f9a | ||
|
|
c424c5abd5 | ||
|
|
101fc593d2 | ||
|
|
cb9e65105e | ||
|
|
9b95bb1222 | ||
|
|
2b01ed9725 | ||
|
|
9b77a1274d | ||
|
|
18d5cca185 | ||
|
|
d5b604c1d0 | ||
|
|
e9b4ccbb93 | ||
|
|
a14f8c961f | ||
|
|
ba311f42c4 | ||
|
|
1b5379d54b | ||
|
|
03a1908ca6 | ||
|
|
f9574d2f83 | ||
|
|
7e45439dfe | ||
|
|
2a8b4ed640 | ||
|
|
86c053e445 | ||
|
|
7eba04bb9a | ||
|
|
b4ac677f67 | ||
|
|
989d44cc5e | ||
|
|
9821875e7a | ||
|
|
07e3b6702b | ||
|
|
31889fbd9c | ||
|
|
87a089a0fa | ||
|
|
88b91e8859 | ||
|
|
f8097e3c72 | ||
|
|
e237c990d8 | ||
|
|
21c0ba975c | ||
|
|
642ce922c3 | ||
|
|
6e5466aacf | ||
|
|
e6635cd844 | ||
|
|
229ab60c57 | ||
|
|
483daee999 | ||
|
|
31ffcd7f7b | ||
|
|
e5139cc00d | ||
|
|
9b1b95b27a | ||
|
|
88d3ceeaf3 | ||
|
|
f74f2f337c | ||
|
|
87b35f240e | ||
|
|
fc861ea304 | ||
|
|
927971ca12 | ||
|
|
0ecb8383e3 | ||
|
|
9c3b2220d5 | ||
|
|
46e15441fd | ||
|
|
2d2a1ca805 | ||
|
|
c577264e1d | ||
|
|
27c3972ab0 | ||
|
|
e4c90730ba | ||
|
|
0694e08399 | ||
|
|
2fefa966dd | ||
|
|
a57cf3df17 | ||
|
|
1ae7260cf3 | ||
|
|
8459eee57d | ||
|
|
f5cebf42ab | ||
|
|
731bdeba7d | ||
|
|
d14cda243a | ||
|
|
0a98aad138 | ||
|
|
1249a08264 | ||
|
|
c0414a5962 | ||
|
|
9cc4d4bb36 | ||
|
|
338358a2ab | ||
|
|
805ba83bb6 | ||
|
|
0f646a8f8d | ||
|
|
2519f09f6f | ||
|
|
60bca9e3d7 | ||
|
|
da8f72fc50 | ||
|
|
c42d4b8967 | ||
|
|
0110854ae4 | ||
|
|
d6d5952cbc | ||
|
|
f9d961cae7 | ||
|
|
a387feea23 | ||
|
|
359c58163b | ||
|
|
0892f5cd1d | ||
|
|
452a7e6dcf | ||
|
|
162dd10a73 | ||
|
|
35120c5364 | ||
|
|
6c828171d2 | ||
|
|
dcc51f8458 | ||
|
|
1287ca62fe | ||
|
|
1ff439fa95 | ||
|
|
ed0f832b2f | ||
|
|
b8e6b2acc6 | ||
|
|
fba32c91c5 | ||
|
|
6505ded12b | ||
|
|
53088affcb | ||
|
|
9c030764fa | ||
|
|
d5d58f1eea | ||
|
|
7181092821 | ||
|
|
fc5ee4f242 | ||
|
|
6e74c4fb14 | ||
|
|
f6a27342e6 | ||
|
|
38b50df2c9 | ||
|
|
db93816e55 | ||
|
|
244c2440fc | ||
|
|
8e2c644669 | ||
|
|
a68809ab31 | ||
|
|
4b4fd80abe | ||
|
|
48ea128267 | ||
|
|
68e5795618 | ||
|
|
1aa20e4196 | ||
|
|
6db25c8f9c | ||
|
|
0ef3db752d | ||
|
|
1a26f5ad34 | ||
|
|
17a5574be7 | ||
|
|
c7bfb8a736 | ||
|
|
7a756c3b98 | ||
|
|
1539e1c3c0 | ||
|
|
a86872c021 | ||
|
|
7d12499f39 | ||
|
|
455434b5f3 | ||
|
|
b66251ab20 | ||
|
|
4a1dfa5cb4 | ||
|
|
1efd65e16c | ||
|
|
908da9cd32 | ||
|
|
e8a30c6ef7 | ||
|
|
df164c104f | ||
|
|
acdf6f6e9a | ||
|
|
f52a63a8f1 | ||
|
|
6a92cb3a16 | ||
|
|
1175451753 | ||
|
|
5c4130411b | ||
|
|
05727eeec5 | ||
|
|
10af949abc | ||
|
|
9851347e74 | ||
|
|
c3e01c8b9c | ||
|
|
a646d9e998 | ||
|
|
3c50a30d4a | ||
|
|
4328ad6b44 | ||
|
|
34b0555508 | ||
|
|
c92cd0058b | ||
|
|
2cfe8b8a27 | ||
|
|
6530fed704 | ||
|
|
7efb203e7b | ||
|
|
1a34376100 | ||
|
|
ec1aa5b393 | ||
|
|
9683d3c001 | ||
|
|
db60545fcc | ||
|
|
392495ef63 | ||
|
|
903764b234 | ||
|
|
72329ed1e4 | ||
|
|
c516b56053 | ||
|
|
5e4295fbfb | ||
|
|
b140c0aa32 | ||
|
|
8f7b293541 | ||
|
|
6f878164c5 | ||
|
|
a16801ff56 | ||
|
|
65a89cf762 | ||
|
|
27d4ada179 | ||
|
|
574edfb246 | ||
|
|
7b7aad9d4e | ||
|
|
c5c9ed8109 | ||
|
|
58fea4090d | ||
|
|
cd0aad6ec1 | ||
|
|
fa45844434 | ||
|
|
c4e2a144b5 | ||
|
|
7fe93c7021 | ||
|
|
1d413a5c2a | ||
|
|
51db52dbdd | ||
|
|
4ade528188 | ||
|
|
64ba48a7c7 | ||
|
|
ac4edb2b97 | ||
|
|
3ea000c98b | ||
|
|
746bef13e1 | ||
|
|
355ab73546 | ||
|
|
67170c70e0 | ||
|
|
f99c2df4e8 | ||
|
|
212ec04fb8 | ||
|
|
89c6089101 | ||
|
|
87bce39d9a | ||
|
|
18900547a0 | ||
|
|
5df2e05a14 | ||
|
|
d4e4173cc3 | ||
|
|
562ad481c5 | ||
|
|
a0ee2a4322 | ||
|
|
2c92efdb43 | ||
|
|
555dc9d6a5 | ||
|
|
27742c5e1b | ||
|
|
280aeb8d2e | ||
|
|
92a868f0fa | ||
|
|
189ac5e09b | ||
|
|
833cb81d65 | ||
|
|
ab09422da5 | ||
|
|
64cad6dc26 | ||
|
|
ce78b493f4 | ||
|
|
c43048ea3f | ||
|
|
6d4d1600ab | ||
|
|
f114e99b9b | ||
|
|
3d587bc86d | ||
|
|
bd8c6be7c6 | ||
|
|
b5d5d508e2 | ||
|
|
3001b65fe8 | ||
|
|
c82771db59 | ||
|
|
9880836c25 | ||
|
|
f12ae28722 | ||
|
|
42b0f0e94f | ||
|
|
0a4b872051 | ||
|
|
b1322105ed | ||
|
|
831ca57fbb | ||
|
|
6502639cd3 | ||
|
|
5059b80db2 | ||
|
|
ecdaf72963 | ||
|
|
28e2ca69ed | ||
|
|
9cad78b6e4 | ||
|
|
af8654666b | ||
|
|
6cd474b35d | ||
|
|
3802ab556c | ||
|
|
3a520eeaf0 | ||
|
|
204f43c112 | ||
|
|
fc8ed8e49e | ||
|
|
56082c308b | ||
|
|
205e563095 | ||
|
|
5b01940c60 | ||
|
|
cbb087e3d1 | ||
|
|
524b926244 | ||
|
|
4059bbfe93 | ||
|
|
9b0b69a4f4 | ||
|
|
10a82eb951 | ||
|
|
690f7dfaf0 | ||
|
|
6a22d20b3a | ||
|
|
b1072bb1fa | ||
|
|
d20da91bab | ||
|
|
17e9692028 | ||
|
|
45dc8a5fe6 | ||
|
|
a237ac1ce4 | ||
|
|
27e66a16b9 | ||
|
|
8f98846bc3 | ||
|
|
bbcab59845 | ||
|
|
3130e07070 | ||
|
|
7bc61ddf4d | ||
|
|
53d15a9011 | ||
|
|
8718e4a0ae | ||
|
|
06a49956af | ||
|
|
38ec131b69 | ||
|
|
1bdaa61dd9 | ||
|
|
080d5055c4 | ||
|
|
423b363964 | ||
|
|
6b9abfccfc | ||
|
|
f589a8da35 | ||
|
|
465581306f | ||
|
|
6b2919961b | ||
|
|
adb6e797b2 | ||
|
|
b410b9438f | ||
|
|
c1d5b4efa9 | ||
|
|
4785dd127e | ||
|
|
9e8e2f7e74 | ||
|
|
c14dfe0f3a | ||
|
|
ed92e7002c | ||
|
|
8f93d9732c | ||
|
|
96f8fa5860 | ||
|
|
bfb857bd5c | ||
|
|
5fe3b5811c | ||
|
|
2a7eff0092 | ||
|
|
1907f5d47e | ||
|
|
aa92350b52 | ||
|
|
869b9bd3b7 | ||
|
|
fe31dcf0a4 | ||
|
|
6b44438f52 | ||
|
|
6b0c654f41 | ||
|
|
c28ff3a891 | ||
|
|
f424b41c3b | ||
|
|
b5f901d8f5 | ||
|
|
29d52492fc | ||
|
|
2d6af20bdc | ||
|
|
59c913ecf8 | ||
|
|
4a50beb4e0 | ||
|
|
c099127272 | ||
|
|
15974b5c54 | ||
|
|
483afe63c4 | ||
|
|
f0cb458a34 | ||
|
|
2c547cd6d0 | ||
|
|
e7282b4a97 | ||
|
|
4fc0ad97fc | ||
|
|
3ae02167b9 | ||
|
|
e407b38565 | ||
|
|
c98d52ed46 | ||
|
|
266913e4b4 | ||
|
|
a3d4a009a5 | ||
|
|
18e00d082e | ||
|
|
6a3979743b | ||
|
|
3a29ad54ca | ||
|
|
987c520160 | ||
|
|
4b6197f6e0 | ||
|
|
01611d4130 | ||
|
|
531aa65fcc | ||
|
|
fb101c4a14 | ||
|
|
7bd97f5f5a | ||
|
|
52c3edd0ea | ||
|
|
368e544051 | ||
|
|
12b2f5a208 | ||
|
|
adc88a8640 | ||
|
|
817db44492 | ||
|
|
f74f7dfa6b | ||
|
|
e9c8730090 | ||
|
|
861ada0396 | ||
|
|
a2fb9c3d81 | ||
|
|
f747461018 | ||
|
|
f408146d57 | ||
|
|
c684001fcf | ||
|
|
6dc53c8c4f | ||
|
|
64ab958c67 | ||
|
|
7a66c9a0c9 | ||
|
|
0ff658632a | ||
|
|
ee32347bd0 | ||
|
|
9e11038e98 | ||
|
|
008ddbb98a | ||
|
|
f084c17d76 | ||
|
|
9dd73aec9d | ||
|
|
3ebf151f3a | ||
|
|
872c194a62 | ||
|
|
d122603c70 | ||
|
|
489fce2031 | ||
|
|
1651151f26 | ||
|
|
aa61ea826f | ||
|
|
0877eb4187 | ||
|
|
6e89cdb3cc | ||
|
|
1eb0b30356 | ||
|
|
417f5dc5d0 | ||
|
|
01a73b9ea3 | ||
|
|
c06788a55c | ||
|
|
fd924107bc | ||
|
|
dce90b91f0 | ||
|
|
aa968b7d25 | ||
|
|
137d089e4a | ||
|
|
bfa27f92e1 | ||
|
|
b405706b50 | ||
|
|
89f44d7bec | ||
|
|
28e9b6ed96 | ||
|
|
19b3eec401 | ||
|
|
c712b3b715 | ||
|
|
5c379c3b13 | ||
|
|
f6f48708d9 | ||
|
|
83890fa5cb | ||
|
|
d7653a6a6f | ||
|
|
676c4c8f1f | ||
|
|
b66870c942 | ||
|
|
af97a0dac2 | ||
|
|
d43ef090ef | ||
|
|
e7451ae567 | ||
|
|
cf4f095624 | ||
|
|
9280809ab0 | ||
|
|
875ae4d43d | ||
|
|
dbd45ac65e | ||
|
|
7c15778ebc | ||
|
|
2699856a75 | ||
|
|
b49c105620 | ||
|
|
2ffbfeec48 | ||
|
|
bd885e25e2 | ||
|
|
e2c6eb142c | ||
|
|
db11e42c6d | ||
|
|
36e5829943 | ||
|
|
7bbb2bbfa7 | ||
|
|
6acd536f43 | ||
|
|
64d1440ad2 | ||
|
|
6f1dd78aa7 | ||
|
|
4330978816 | ||
|
|
935603b852 | ||
|
|
9671938a66 | ||
|
|
5cc974eb9c | ||
|
|
e526cc6750 | ||
|
|
34fea78b01 | ||
|
|
072547005c | ||
|
|
cdb5852e8b | ||
|
|
050477c19f | ||
|
|
52b24bb3e9 | ||
|
|
df8a063369 | ||
|
|
3fc4a788b8 | ||
|
|
15e5108ec7 | ||
|
|
b8fa694e26 | ||
|
|
f1b55cba79 | ||
|
|
e76dd78cd5 | ||
|
|
09a668493b | ||
|
|
1ef466d2a4 | ||
|
|
e15ddbf2d4 | ||
|
|
1c0ce073e5 | ||
|
|
5964be18ed | ||
|
|
06ad4f106b | ||
|
|
adf343468f | ||
|
|
5a0bb3ce03 | ||
|
|
b3edb2833e | ||
|
|
83c2017544 | ||
|
|
42ecf8ec2a | ||
|
|
fe53c28f77 | ||
|
|
44e8b395f5 | ||
|
|
13828897a2 | ||
|
|
965a9898ca | ||
|
|
d3c9d1e7e2 | ||
|
|
ff3df14a90 | ||
|
|
374f477645 | ||
|
|
11bcbc7918 | ||
|
|
f91f77dba0 | ||
|
|
ac36bc7c6c | ||
|
|
184aa7cd83 | ||
|
|
e20a80b410 | ||
|
|
43f5cbdcf1 | ||
|
|
7c8d6067f5 | ||
|
|
5500239146 | ||
|
|
62844f3111 | ||
|
|
37df1c6376 | ||
|
|
abe1dea027 | ||
|
|
66500050fe | ||
|
|
2bc2f11fb1 | ||
|
|
4b88c78036 | ||
|
|
5a98301b7d | ||
|
|
2cf7fd4ffc | ||
|
|
83349e1c37 | ||
|
|
d928991ca5 | ||
|
|
8b3cd0fcee | ||
|
|
2afd7483df | ||
|
|
ae108d1bed | ||
|
|
37e343e92e | ||
|
|
8b01a12ef9 | ||
|
|
91ea80fbde | ||
|
|
deec676180 | ||
|
|
5fb22a505f | ||
|
|
539a1300d2 | ||
|
|
915fe7b134 | ||
|
|
da393a66c1 | ||
|
|
6563b93721 | ||
|
|
c8e51ab82e | ||
|
|
435fbe721d | ||
|
|
3677e90ad1 | ||
|
|
cd96a87028 | ||
|
|
3b1e5244e4 | ||
|
|
07a1baff36 | ||
|
|
c87f6d5a0d | ||
|
|
36f6c41059 | ||
|
|
fa0170fa16 | ||
|
|
c66936861f | ||
|
|
8933c39ce0 | ||
|
|
34865b5bb8 | ||
|
|
271ebed7b6 | ||
|
|
08e7eca34f | ||
|
|
0162bcdaaf | ||
|
|
39e76862d5 | ||
|
|
a7aeffa52a | ||
|
|
9f847ca1db | ||
|
|
6f4a429123 | ||
|
|
60db6eb445 | ||
|
|
5c06fec00a | ||
|
|
12eb80d4d7 | ||
|
|
c371bf323c | ||
|
|
8dca20c73d | ||
|
|
aabfb5273e | ||
|
|
a282382402 | ||
|
|
e86733052b | ||
|
|
a112036c2e | ||
|
|
6c07bfb5a3 | ||
|
|
7668a3e881 | ||
|
|
f53f2c724d | ||
|
|
5ea6f9307c | ||
|
|
8bdaa232ca | ||
|
|
1ad945a1a9 | ||
|
|
85d11e7896 | ||
|
|
bcd940d808 | ||
|
|
dd837b96e8 | ||
|
|
57295756fc | ||
|
|
9f1307f559 | ||
|
|
c489764b06 | ||
|
|
845b1b2022 | ||
|
|
3662a66ec9 | ||
|
|
e1e0083633 | ||
|
|
58e4624cf5 | ||
|
|
df069ebcdc | ||
|
|
82f2c476d8 | ||
|
|
3545cac908 | ||
|
|
cb95b833e6 | ||
|
|
e2c1c44537 | ||
|
|
aeb0587263 | ||
|
|
3471876687 | ||
|
|
82d58564ee | ||
|
|
d3918a487a | ||
|
|
a25232be6a | ||
|
|
bfa05f943a | ||
|
|
dc40a54999 | ||
|
|
682621e196 | ||
|
|
41fa678591 | ||
|
|
e7557da944 | ||
|
|
c7c9885e18 | ||
|
|
62c665a598 | ||
|
|
6d687d391d | ||
|
|
2ac6b89368 | ||
|
|
b8d4829e02 | ||
|
|
a9b4355ebf | ||
|
|
b7f3ecdc5d | ||
|
|
0428280480 | ||
|
|
31e7fb9a22 | ||
|
|
b3fc768ea7 | ||
|
|
7b990e798d | ||
|
|
7e1e00d246 | ||
|
|
15677e948d | ||
|
|
457f85d697 | ||
|
|
f3a3df648f | ||
|
|
000f3dd680 | ||
|
|
db3b0e39b5 | ||
|
|
e5a5cf9f7e | ||
|
|
0c3853de2b | ||
|
|
0146e594de | ||
|
|
1a790bca41 | ||
|
|
fc039ebe93 | ||
|
|
22561f6848 | ||
|
|
8a7ccc07ae | ||
|
|
b43f7b9f31 | ||
|
|
69b3086c65 | ||
|
|
e5b05b5859 | ||
|
|
a07994adb1 | ||
|
|
8d83b45657 | ||
|
|
22371715c5 | ||
|
|
8a7d042476 | ||
|
|
26205cdd67 | ||
|
|
ccb9cd5522 | ||
|
|
2383214b7e | ||
|
|
a82b6bf0e9 | ||
|
|
3e541e65ed | ||
|
|
314becdc45 | ||
|
|
8f662ced5c | ||
|
|
3ff782e128 | ||
|
|
6a6a708ee7 | ||
|
|
1751a77e6f | ||
|
|
3069244f21 | ||
|
|
99d4232304 | ||
|
|
ec408316bd | ||
|
|
eee5271966 | ||
|
|
34e4816517 | ||
|
|
7bde6dedb5 | ||
|
|
6864f57917 | ||
|
|
0b705ca897 | ||
|
|
4661d72f8c | ||
|
|
2b7053c724 | ||
|
|
8d81367354 | ||
|
|
facb358404 | ||
|
|
fd49e101ca | ||
|
|
a55e19bb4a | ||
|
|
07c1893895 | ||
|
|
eed3c37bcb | ||
|
|
42d9a54668 | ||
|
|
51811452fd | ||
|
|
0b21958826 | ||
|
|
f3fdc68dce | ||
|
|
3b76a2abdd | ||
|
|
87964d42a7 | ||
|
|
2529195097 | ||
|
|
62e54fe9ea | ||
|
|
ad49a0659e | ||
|
|
a703fba542 | ||
|
|
bc57b549d5 | ||
|
|
eed64e0f60 | ||
|
|
a673050489 | ||
|
|
d07890112d | ||
|
|
5055c89cde | ||
|
|
eb04cab549 | ||
|
|
db2e56a382 | ||
|
|
937929c3c5 |
No files matched your search
@@ -9,7 +9,7 @@ body:
|
||||
Please make sure you've checked the following things first:
|
||||
|
||||
- You're using the latest release of AdvancedServerList [[Download from Modrinth](https://modrinth.com/plugin/advancedserverlist)]
|
||||
- You're using a supported Proxy/Server version. As of right now is only 1.19.x supported.
|
||||
- You're using a supported Proxy/Server version. As of right now is only 1.19+ supported.
|
||||
- The issue happens with just AdvancedServerList installed.
|
||||
- type: dropdown
|
||||
attributes:
|
||||
@@ -0,0 +1,11 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: API Support
|
||||
about: For API support, please create an issue in the `asl-api` repository.
|
||||
url: https://codeberg.org/Andre601/asl-api
|
||||
- name: Discord Server
|
||||
about: Please ask questions in the `#asl-support` channel on the M.O.S.S. Discord Server.
|
||||
url: https://discord.gg/MyPTjpsgGH
|
||||
- name: Wiki
|
||||
about: Check the Wiki for explanations about the features and examples.
|
||||
url: https://asl.andre601.ch
|
||||
@@ -9,7 +9,7 @@ body:
|
||||
Please make sure you've checked the following things first:
|
||||
|
||||
- You're using the latest release of AdvancedServerList [[Download from Modrinth](https://modrinth.com/plugin/advancedserverlist)]
|
||||
- You're using a supported Proxy/Server version. As of right now is only 1.19.x supported.
|
||||
- You're using a supported Proxy/Server version. As of right now is only 1.19+ supported.
|
||||
- The Feature you suggest hasn't been suggested before.
|
||||
- type: dropdown
|
||||
attributes:
|
||||
@@ -0,0 +1,103 @@
|
||||
<center><strong><u><font size="6">AdvancedServerList</font ></u></strong></center>
|
||||
|
||||
AdvancedServerList is a plugin designed to allow the largest possible customization for your server's appearance in a Player's multiplayer screen.
|
||||
The plugin offers extensive customization including changing the MOTD, player count text, player count hover and favicon of your server. All while also allowing you to create multiple profiles with priorities and conditions to set when a specific profile should be displayed.
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
<a href="https://papermc.io" target="_blank">
|
||||
<img src="https://wsrv.nl/?url=https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/supported/paper_vector.svg&h=48" alt="platform-paper" title="Tested on Paper">
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://www.spigotmc.org" target="_blank">
|
||||
<img src="https://wsrv.nl/?url=https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/supported/bungeecord_vector.svg&h=48" alt="platform-bungeecord" title="Tested on BungeeCord">
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://www.papermc.io" target="_blank">
|
||||
<img src="https://wsrv.nl/?url=https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/supported/waterfall_vector.svg&h=48" alt="platform-waterfall" title="Tested on Waterfall">
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://velocitypowered.com" target="_blank">
|
||||
<img src="https://wsrv.nl/?url=https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/supported/velocity_vector.svg&h=48" alt="platform-velocity" title="Tested on Velocity">
|
||||
</a>
|
||||
|
||||
## Server List Profiles
|
||||
|
||||
A Server List Profile is a single YAML file located in the plugin's `profiles` folder. It contains settings that allow you to modify the server's appearance within a Player's multiplayer screen.
|
||||
|
||||
### Priority and Conditions
|
||||
|
||||
Each file has a priority and optional condition. The priority is used to determine what file AdvancedServerList should use first - starting from highest number to lowest - with conditions being used to set when the profile should be used.
|
||||
Once a profile has been found with a condition returning true - which is the default should there be no condition - will AdvancedServerList use said profile.
|
||||
|
||||
> *Read more about [Priority](https://asl.andre601.ch/profiles/#priority) and [Conditions](https://asl.andre601.ch/profiles/#condition)*
|
||||
|
||||
### Placeholders
|
||||
|
||||
AdvancedServerList supports two sets of placeholders. The first one being its own placeholders using the `${<type> <values>}` format adobted from BungeeTabListPlus and the second one being placeholders from PlaceholderAPI using its `%<identifier>_<values>%` placeholder format.
|
||||
|
||||
Note that internal placeholders can be used in all text-based options of a profile, while PlaceholderAPI placeholders can be used in all text-based options except the `condition` one.
|
||||
As a final note can plugins provide their own placeholders to use within AdvancedServerList by using its API.
|
||||
|
||||
> *Read more about [Placeholders](https://asl.andre601.ch/profiles/placeholders/)*
|
||||
|
||||
### Formatting
|
||||
|
||||
AdvancedServerList utilizes the MiniMessage formatting to handle colors and formatting of text. This option was chosen over legacy formatting - using `&` character - for being more clear and readable in its design, not to mention having an easier way for more complex features such as RGB gradients.
|
||||
|
||||
> *Read more about [Formatting](https://asl.andre601.ch/profiles/formatting/)*
|
||||
|
||||
## Dependencies
|
||||
|
||||
AdvancedServerList soft-depends on the following plugins, meaning their inclusion is not required.
|
||||
|
||||
| Plugin | Supported Platforms |
|
||||
|-------------------------------------------------------------------------|----------------------------------------|
|
||||
| [PlaceholderAPI](https://hangar.papermc.io/HelpChat/PlaceholderAPI) | Paper |
|
||||
| [ViaVersion](https://hangar.papermc.io/ViaVersion/ViaVersion) | Paper |
|
||||
| [PAPIProxyBridge](https://hangar.papermc.io/William278/PAPIProxyBridge) | BungeeCord, Waterfall, Velocity |
|
||||
| [Maintenance](https://hangar.papermc.io/kennytv/Maintenance) | Paper, BungeeCord, Waterfall, Velocity |
|
||||
|
||||
## Statistics
|
||||
|
||||
The plugin sends statistics to [bStats](https://bstats.org) to show.
|
||||
You can disable the sending of statistics in the global bStats config located inside `/plugins/bstats/`.
|
||||
|
||||
You can find the bStats pages of the plugin for the following platforms:
|
||||
|
||||
- [Paper](https://bstats.org/plugin/bungeecord/AdvancedServerList/15584)
|
||||
- [BungeeCord, Waterfall](https://bstats.org/plugin/bungeecord/AdvancedServerList/15585)
|
||||
- [Velocity](https://bstats.org/plugin/velocity/AdvancedServerList/15587)
|
||||
|
||||
## Videos
|
||||
|
||||
Below can you find a collection of videos made for my plugin. If you made a video and it isn't shown here, let me know and I'll add it.
|
||||
|
||||
<details><summary>Click to show/hide Videos</summary>
|
||||
<p>
|
||||
|
||||
Spanish Video by Ajneb97
|
||||
<iframe width="560" height="auto" src="https://www.youtube.com/embed/rIbljm_4HVI" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
## Screenshots
|
||||
|
||||
<a href="https://modrinth.com/plugin/advancedserverlist/gallery" target="_blank">
|
||||
<img src="https://wsrv.nl/?url=https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/documentation/modrinth-gallery_vector.svg&h=48" alt="gallery" title="Check out the Gallery">
|
||||
</a>
|
||||
|
||||
## Support
|
||||
|
||||
<a href="https://discord.gg/MyPTjpsgGH" target="_blank">
|
||||
<img src="https://wsrv.nl/?url=https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/social/discord-singular_vector.svg&h=48" alt="discord" title="Join the M.O.S.S. Discord Server">
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://app.revolt.chat/invite/74TpERXA" target="_blank">
|
||||
<img src="https://wsrv.nl/?url=https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/social/revolt-singular_vector.svg&h=48" alt="revolt" title="Join my Revolt Server">
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://blobfox.coffee/@andre_601" target="_blank">
|
||||
<img src="https://wsrv.nl/?url=https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/social/mastodon-singular_vector.svg&h=48" alt="revolt" title="Chat with me on Mastodon">
|
||||
</a>
|
||||
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 876 KiB |
|
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 877 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
@@ -0,0 +1,37 @@
|
||||
name: Pull Request
|
||||
description: Create a new Pull request
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |-
|
||||
Thank you for creating a Pull request!
|
||||
|
||||
Please be sure to fill out this template with all requested information to ensure it being valid and not being closed unmerged.
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Type
|
||||
description: What kind of Pull request is this?
|
||||
options:
|
||||
- Enhancement
|
||||
- Bugfix
|
||||
- Documentation
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Breaking Change?
|
||||
description: |-
|
||||
Is this a breaking change?
|
||||
A breaking change means that it becomes incompatible with previous server/proxy versions or plugin versions.
|
||||
options:
|
||||
- Yes
|
||||
- No
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: Provide a detailed description of what this Pull request is doing. Link any issues this Pull request may fix/close.
|
||||
validations:
|
||||
required: true
|
||||
@@ -1,2 +0,0 @@
|
||||
patreon: andre_601
|
||||
ko_fi: andre_601
|
||||
@@ -1,41 +0,0 @@
|
||||
name: API support
|
||||
description: Ask for help with the API.
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |-
|
||||
## Before opening
|
||||
Please make sure you've checked the following things first:
|
||||
|
||||
- You're using the latest release of AdvancedServerList [[Download from Modrinth](https://modrinth.com/plugin/advancedserverlist)] and the right API version.
|
||||
- You're using a supported Proxy/Server version. As of right now is only 1.19.x supported.
|
||||
- The Feature you suggest hasn't been suggested before.
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Platforms
|
||||
description: On what platform(s) are you using the API?
|
||||
multiple: true
|
||||
options:
|
||||
- Proxy (BungeeCord, Waterfall, ...)
|
||||
- Proxy (Velocity)
|
||||
- Server (Spigot, Paper, ...)
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: |-
|
||||
Give a detailed description of what exactly you try to do, what steps you did and what you need help with.
|
||||
placeholder: I tried to...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Code
|
||||
description: |-
|
||||
Provide code-snippets to help us better understand what you tried.
|
||||
The code is automatically parsed as Java Code.
|
||||
render: java
|
||||
validations:
|
||||
required: true
|
||||
@@ -1,8 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Discord Server
|
||||
about: Ask questions about the plugin in the `#advancedserverlist` channel.
|
||||
url: https://discord.gg/6dazXp6
|
||||
- name: Wiki
|
||||
about: Check the Wiki for explanations about the features and examples.
|
||||
url: https://github.com/Andre601/AdvancedServerList/wiki
|
||||
@@ -1,108 +0,0 @@
|
||||
# AdvancedServerList
|
||||
AdvancedServerList is a plugin that allowys you to customize your server's MOTD, favicon, Player count text and player count hover displayed in a player's multiplayer server list based on conditions and priorities.
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
<a href="https://www.spigotmc.org" target="_blank">
|
||||
<img src="https://wsrv.nl/?url=https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact-minimal/supported/spigot_vector.svg&w=64 alt="platform-spigot" title="Tested on Spigot">
|
||||
</a>
|
||||
<a href="https://papermc.io" target="_blank">
|
||||
<img src="https://wsrv.nl/?url=https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact-minimal/supported/paper_vector.svg&w=64 alt="platform-paper" title="Tested on Paper">
|
||||
</a>
|
||||
<a href="https://www.spigotmc.org" target="_blank">
|
||||
<img src="https://wsrv.nl/?url=https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact-minimal/supported/bungeecord_vector.svg&w=64 alt="platform-bungeecord" title="Tested on BungeeCord">
|
||||
</a>
|
||||
<a href="https://www.papermc.io" target="_blank">
|
||||
<img src="https://wsrv.nl/?url=https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact-minimal/supported/waterfall_vector.svg&w=64 alt="platform-waterfall" title="Tested on Waterfall">
|
||||
</a>
|
||||
<a href="https://velocitypowered.com" target="_blank">
|
||||
<img src="https://wsrv.nl/?url=https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact-minimal/supported/velocity_vector.svg&w=64 alt="platform-velocity" title="Tested on Velocity">
|
||||
</a>
|
||||
|
||||
## What is a Server List Profile?
|
||||
A Server List Profile refers to a YAML file located in the plugin's `profiles` directory.
|
||||
It allows you to configure specific aspects of the server display in the MC-Client's multiplayer server list, such as the MOTD, player count text, player count hover or Favicon.
|
||||
|
||||
> [*Read More*][profiles]
|
||||
|
||||
### Conditions
|
||||
Thanks to conditions can you determine when a profile should be displayed in the player’s server list, to allow displaying specific text (i.e. a warning when they are using an outdated version).
|
||||
|
||||
When multiple profiles exist that have valid conditions will the one with the highest priority (Higher number = higher priority) be selected. Having no conditions makes the profile automatically return true.
|
||||
|
||||
> [*Read More*][conditions]
|
||||
|
||||
### Placeholders
|
||||
Placeholders can be used within conditions to further customize them. They can also be used in any other text option (motd, playerCount and players) to display their respective values.
|
||||
The format is `${<target> <identifier>}` which was adopted from BungeeTabListPlus. Depending on what platform you use the plugin on are only specific placeholders available.
|
||||
|
||||
When using the SpigotMC/PaperMC version can you also use placeholders from [PlaceholderAPI][placeholderapi] using its `%<identifier>_<values>%` placeholder format. PlaceholderAPI needs to be installed for that.
|
||||
|
||||
> [*Read More*][placeholders]
|
||||
|
||||
### Formatting
|
||||
The plugin uses the MiniMessage format for a consistent, easy way of formatting your text.
|
||||
Only certain options are usable for each option. As an example does motd support HEX colours and gradients, while the other options only support default colour codes.
|
||||
|
||||
> [*Read More*][minimessage]
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Dependency | Platforms | Required? |
|
||||
|------------------|---------------|-----------|
|
||||
| [ProtocolLib] | Spigot | Yes |
|
||||
| [PlaceholderAPI] | Spigot, Paper | No |
|
||||
| [ViaVersion] | Spigot, Paper | No |
|
||||
|
||||
## Statistics
|
||||
|
||||
This plugin sends statistics to [bStats] to display.
|
||||
You can disable this in the global bStats config file located in `/plugins/bstats/`
|
||||
|
||||
- [BungeeCord][bstats-bungee]
|
||||
- [Paper/Spigot][bstats-spigot]
|
||||
- [Velocity][bstats-velocity]
|
||||
|
||||
## Videos
|
||||
Do you have your own video to share? Contact me and I will add it to this section.
|
||||
|
||||
<details><summary>Click to Show/Hide Videos</summary>
|
||||
|
||||
Spanish Video by Ajneb97
|
||||
<iframe width="560" height="auto" src="https://www.youtube.com/embed/rIbljm_4HVI" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
|
||||
|
||||
</details>
|
||||
|
||||
## Screenshots
|
||||
|
||||
<a href="https://modrinth.com/plugin/advancedserverlist/gallery" target="_blank">
|
||||
<img src="https://wsrv.nl/?url=https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/documentation/modrinth-gallery_vector.svg&w=208" alt="gallery" title="Check out the Gallery">
|
||||
</a>
|
||||
|
||||
## Support
|
||||
<a href="https://discord.gg/6dazXp6" target="_blank">
|
||||
<img src="https://wsrv.nl/?url=https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact-minimal/social/discord-singular_vector.svg&w=64" alt="discord" title="Join my Discord Server">
|
||||
</a>
|
||||
<a href="https://app.revolt.chat/invite/74TpERXA" target="_blank">
|
||||
<img src="https://wsrv.nl/?url=https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact-minimal/social/revolt-singular_vector.svg&w=64" alt="revolt" title="Join my Revolt Server">
|
||||
</a>
|
||||
<a href="https://blobfox.coffee/@andre_601" target="_blank">
|
||||
<img src="https://wsrv.nl/?url=https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact-minimal/social/mastodon-singular_vector.svg&w=64" alt="revolt" title="Chat with me on Mastodon">
|
||||
</a>
|
||||
|
||||
<!-- Links -->
|
||||
[profiles]: https://andre601.ch/AdvancedServerList/latest/profiles
|
||||
[conditions]: https://andre601.ch/AdvancedServerList/latest/profiles/#conditions
|
||||
[placeholders]: https://andre601.ch/AdvancedServerList/latest/profiles/placeholders
|
||||
[minimessage]: https://andre601.ch/AdvancedServerList/latest/profiles/formatting
|
||||
|
||||
[protocollib]: https://www.spigotmc.org/resources/1997/
|
||||
[placeholderapi]: https://www.spigotmc.org/resources/6245/
|
||||
[viaversion]: https://www.spigotmc.org/resources/19254/
|
||||
|
||||
[bstats]: https://bstats.org
|
||||
[bstats-bungee]: https://bstats.org/plugin/bungeecord/AdvancedServerList/15585
|
||||
[bstats-spigot]: https://bstats.org/plugin/bukkit/AdvancedServerList/15584
|
||||
[bstats-velocity]: https://bstats.org/plugin/velocity/AdvancedServerList/15587
|
||||
|
||||
[gallery]: https://modrinth.com/mod/advancedserverlist/gallery
|
||||
@@ -1,50 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: maven
|
||||
directory: "/bungeecord"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
||||
labels:
|
||||
- 'Type: Update Dependency'
|
||||
- 'Target: BungeeCord'
|
||||
- package-ecosystem: maven
|
||||
directory: "/core"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
||||
labels:
|
||||
- 'Type: Update Dependency'
|
||||
- 'Target: Core'
|
||||
- package-ecosystem: maven
|
||||
directory: "/bukkit"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
||||
labels:
|
||||
- 'Type: Update Dependency'
|
||||
- 'Target: Bukkit'
|
||||
- package-ecosystem: maven
|
||||
directory: "/velocity"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
||||
labels:
|
||||
- 'Type: Update Dependency'
|
||||
- 'Target: Velocity'
|
||||
- package-ecosystem: github-actions
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
||||
labels:
|
||||
- 'Type: Update Dependency'
|
||||
- 'Target: GitHub Action'
|
||||
- package-ecosystem: pip
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
||||
labels:
|
||||
- 'Type: Update Dependency'
|
||||
- 'Target: MkDocs'
|
||||
@@ -1,14 +0,0 @@
|
||||
"Target: All":
|
||||
- all: ["bukkit/**", "bungeecord/**", "core/**", "velocity/**"]
|
||||
|
||||
"Target: Bukkit":
|
||||
- all: ["bukkit/**", "!bungeecord/**", "!core/**", "!velocity/**"]
|
||||
|
||||
"Target: BungeeCord":
|
||||
- all: ["!bukkit/**", "bungeecord/**", "!core/**", "!velocity/**"]
|
||||
|
||||
"Target: Core":
|
||||
- all: ["!bukkit/**", "!bungeecord/**", "core/**", "!velocity/**"]
|
||||
|
||||
"Target: Velocity":
|
||||
- all: ["!bukkit/**", "!bungeecord/**", "!core/**", "velocity/**"]
|
||||
@@ -1,100 +0,0 @@
|
||||
name: Create PR Artifacts
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- '.github/**'
|
||||
- 'docs/**'
|
||||
- '.gitignore'
|
||||
- 'LICENSE'
|
||||
- 'README.md'
|
||||
- 'mkdocs.yml'
|
||||
- 'requirements.txt'
|
||||
|
||||
concurrency:
|
||||
group: '${{ github.workflow }}-${{ github.head_ref }}'
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
buildArtifacts:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
comment_id: ${{ steps.comment.outputs.comment-id }}
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
||||
- name: Find Comment
|
||||
uses: peter-evans/find-comment@v2
|
||||
id: find-comment
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-author: 'github-actions[bot]'
|
||||
body-includes: '<!-- pr-build comment -->'
|
||||
- name: Print Comment info
|
||||
run: |
|
||||
echo "Comment ID: ${{ steps.find-comment.outputs.comment-id }}"
|
||||
echo "Comment Author: ${{ steps.find-comment.outputs.comment-author }}"
|
||||
echo "Comment created at: ${{ steps.find-comment.outputs.comment-created-at }}"
|
||||
- name: Post/Update Comment
|
||||
uses: peter-evans/create-or-update-comment@v3
|
||||
id: comment
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-id: ${{ steps.find-comment.outputs.comment-id }}
|
||||
edit-mode: replace
|
||||
body-path: .github/assets/pr-comments/building.md
|
||||
- name: Set up JDK 1.16
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '16'
|
||||
cache: 'maven'
|
||||
- name: Obtain short Commit SHA
|
||||
id: get-commit
|
||||
run: 'echo "sha_short=$(git rev-parse --short ${{ github.event.pull_request.head.sha }})" >> $GITHUB_OUTPUT'
|
||||
- name: Build Jar files
|
||||
run: mvn clean install
|
||||
env:
|
||||
COMMIT_SHA: ${{ steps.get-commit.outputs.sha_short }}
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: AdvancedServerList-Artifacts
|
||||
path: '*/target/AdvancedServerList-*.jar'
|
||||
if-no-files-found: error
|
||||
commentOnFail:
|
||||
needs: [buildArtifacts]
|
||||
if: failure()
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: refs/pull/${{ github.event.pull_request.number }}/merge
|
||||
- name: Render Markdown Template
|
||||
id: render
|
||||
uses: chuhlomin/render-template@v1.7
|
||||
with:
|
||||
template: './.github/assets/pr-comments/failed.md'
|
||||
vars: |
|
||||
commit: ${{ github.event.pull_request.head.sha }}
|
||||
logs: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
- name: Update Comment (Failure)
|
||||
uses: peter-evans/create-or-update-comment@v3
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-id: ${{ needs.buildArtifacts.outputs.comment_id }}
|
||||
edit-mode: replace
|
||||
body: ${{ steps.render.outputs.result }}
|
||||
@@ -1,39 +0,0 @@
|
||||
name: 'Deploy Site (Push)'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- 'mkdocs.yml'
|
||||
- 'mkdocs-theme/**'
|
||||
- 'requirements.txt'
|
||||
branches:
|
||||
- master
|
||||
tags-ignore:
|
||||
- '**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python 3.7
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: 3.x
|
||||
- name: Create/Get Cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
key: ${{ github.ref }}
|
||||
path: .cache
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip setuptools
|
||||
python -m pip install -r requirements.txt
|
||||
- name: Set up Git
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "actions@github.com"
|
||||
- name: Deploy Files
|
||||
run: mkdocs gh-deploy --force
|
||||
@@ -1,99 +0,0 @@
|
||||
name: Comment on Pull request
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
types:
|
||||
- completed
|
||||
workflows:
|
||||
- 'Create PR Artifacts'
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
comment:
|
||||
if: github.event.workflow_run.conclusion == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Get Artifact and Pull request info
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }} # Needs to be set as env variable so that 'gh api' can be used.
|
||||
WORKFLOW_RUN_EVENT_OBJ: ${{ toJSON(github.event.workflow_run) }}
|
||||
OWNER: ${{ github.repository_owner }}
|
||||
REPO: ${{ github.event.repository.name }}
|
||||
run: |
|
||||
PREVIOUS_JOB_ID=$(jq -r '.id' <<< "$WORKFLOW_RUN_EVENT_OBJ")
|
||||
echo "Previous Job ID: $PREVIOUS_JOB_ID"
|
||||
echo "PREVIOUS_JOB_ID=$PREVIOUS_JOB_ID" >> "$GITHUB_ENV"
|
||||
|
||||
SUITE_ID=$(jq -r '.check_suite_id' <<< "$WORKFLOW_RUN_EVENT_OBJ")
|
||||
echo "Previous Suite ID: $SUITE_ID"
|
||||
echo "SUITE_ID=$SUITE_ID" >> "$GITHUB_ENV"
|
||||
|
||||
ARTIFACT_ID=$(gh api "/repos/$OWNER/$REPO/actions/artifacts" \
|
||||
--jq ".artifacts.[] |
|
||||
select(.workflow_run.id==${PREVIOUS_JOB_ID}) |
|
||||
select(.expired==false) |
|
||||
.id")
|
||||
|
||||
echo "Artifact ID: $ARTIFACT_ID"
|
||||
echo "ARTIFACT_ID=$ARTIFACT_ID" >> "$GITHUB_ENV"
|
||||
|
||||
ARTIFACT_EXPIRE_DATE=$(gh api "/repos/$OWNER/$REPO/actions/artifacts" \
|
||||
--jq ".artifacts.[] |
|
||||
select(.workflow_run.id==${PREVIOUS_JOB_ID}) |
|
||||
select(.expired==false) |
|
||||
.expires_at")
|
||||
|
||||
echo "Artifact Expire Date: $ARTIFACT_EXPIRE_DATE"
|
||||
echo "ARTIFACT_EXPIRE_DATE=$ARTIFACT_EXPIRE_DATE" >> "$GITHUB_ENV"
|
||||
|
||||
PR_NUMBER=$(jq -r '.pull_requests[0].number' \
|
||||
<<< "$WORKFLOW_RUN_EVENT_OBJ")
|
||||
|
||||
echo "Pull request Number: $PR_NUMBER"
|
||||
echo "PR_NUMBER=$PR_NUMBER" >> "$GITHUB_ENV"
|
||||
|
||||
HEAD_SHA=$(jq -r '.pull_requests[0].head.sha' \
|
||||
<<< "$WORKFLOW_RUN_EVENT_OBJ")
|
||||
|
||||
echo "Head SHA: $HEAD_SHA"
|
||||
echo "HEAD_SHA=$HEAD_SHA" >> "$GITHUB_ENV"
|
||||
- name: Find Comment
|
||||
uses: peter-evans/find-comment@v2
|
||||
id: find-comment
|
||||
with:
|
||||
issue-number: ${{ env.PR_NUMBER }}
|
||||
comment-author: 'github-actions[bot]'
|
||||
body-includes: '<!-- pr-build comment -->'
|
||||
- name: Print Comment info
|
||||
run: |
|
||||
echo "Comment ID: ${{ steps.find-comment.outputs.comment-id }}"
|
||||
echo "Comment Author: ${{ steps.find-comment.outputs.comment-author }}"
|
||||
echo "Comment created at: ${{ steps.find-comment.outputs.comment-created-at }}"
|
||||
- name: Render Markdown Template
|
||||
id: render
|
||||
uses: chuhlomin/render-template@v1.7
|
||||
env:
|
||||
JOB_PATH: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ env.PREVIOUS_JOB_ID }}"
|
||||
ARTIFACT_URL: "https://nightly.link/${{ github.repository }}/suites/${{ env.SUITE_ID }}/artifacts/${{ env.ARTIFACT_ID }}"
|
||||
ARTIFACT_EXPIRE_DATE: "${{ env.ARTIFACT_EXPIRE_DATE }}"
|
||||
HEAD_SHA: "${{ env.HEAD_SHA }}"
|
||||
with:
|
||||
template: './.github/assets/pr-comments/success.md'
|
||||
timezone: Europe/Zurich
|
||||
vars: |
|
||||
commit: ${{ env.HEAD_SHA }}
|
||||
logs: ${{ env.JOB_PATH }}
|
||||
download: ${{ env.ARTIFACT_URL }}
|
||||
expire: ${{ env.ARTIFACT_EXPIRE_DATE }}
|
||||
- name: Update Comment
|
||||
uses: peter-evans/create-or-update-comment@v3
|
||||
with:
|
||||
issue-number: ${{ env.PR_NUMBER }}
|
||||
comment-id: ${{ steps.find-comment.outputs.comment-id }}
|
||||
edit-mode: replace
|
||||
body: ${{ steps.render.outputs.result }}
|
||||
@@ -1,119 +0,0 @@
|
||||
#
|
||||
# Big thank you to @Kir-Antipov for providing this workflow file.
|
||||
# Check out his mc-publish action here: https://github.com/Kir-Antipov/mc-publish
|
||||
#
|
||||
|
||||
name: Publish Assets
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
jobs:
|
||||
buildJars:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
plugin_release: ${{ steps.github-release.outputs.PLUGIN_RELEASE }}
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Get Version from Release
|
||||
id: github-release
|
||||
run: |
|
||||
version=$(echo ${{ github.event.release.tag_name }} | cut -d'v' -f2)
|
||||
echo "PLUGIN_RELEASE=$version" >> $GITHUB_OUTPUT
|
||||
- name: Set up JDK 1.16
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '16'
|
||||
cache: 'maven'
|
||||
- name: Build Jar files
|
||||
run: mvn clean install
|
||||
- name: Save All files
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: asl-files
|
||||
path: |
|
||||
*/target/AdvancedServerList-*.jar
|
||||
hangar-uploader/target/HangarUploader.jar
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
publish:
|
||||
needs: [buildJars]
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- platforms: BungeeCord, Waterfall
|
||||
directory: bungeecord
|
||||
loaders: |
|
||||
bungeecord
|
||||
waterfall
|
||||
- platforms: Spigot, Paper
|
||||
directory: bukkit
|
||||
loaders: |
|
||||
spigot
|
||||
paper
|
||||
- platforms: Velocity
|
||||
directory: velocity
|
||||
loaders: velocity
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Get Jar files
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: asl-files
|
||||
- name: Upload AdvancedServerList v${{ needs.buildJars.outputs.plugin_release }} (${{ matrix.loaders }}) to GitHub and Modrinth
|
||||
if: '!github.event.release.prerelease'
|
||||
uses: Kir-Antipov/mc-publish@v3.3
|
||||
with:
|
||||
name: ${{ github.event.release.tag_name }} (${{ matrix.platforms }})
|
||||
version: ${{ needs.buildJars.outputs.plugin_release }}
|
||||
version-type: release
|
||||
files: ${{ matrix.directory}}/target/AdvancedServerList-*.jar
|
||||
loaders: ${{ matrix.loaders }}
|
||||
game-version-filter: releases
|
||||
game-versions: '>=1.19'
|
||||
java: 16
|
||||
modrinth-id: xss83sOY
|
||||
modrinth-featured: false
|
||||
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload AdvancedServerList v${{ needs.buildJars.outputs.plugin_release }} (${{ matrix.loaders }}) to GitHub and Modrinth [Pre-Release]
|
||||
if: 'github.event.release.prerelease'
|
||||
uses: Kir-Antipov/mc-publish@v3.3
|
||||
with:
|
||||
name: ${{ github.event.release.tag_name }} (${{ matrix.platforms }})
|
||||
version: ${{ needs.buildJars.outputs.plugin_release }}
|
||||
version-type: beta
|
||||
files: ${{ matrix.directory }}/target/AdvancedServerList-*.jar
|
||||
loaders: ${{ matrix.loaders }}
|
||||
game-version-filter: releases
|
||||
game-versions: '>=1.19'
|
||||
java: 16
|
||||
modrinth-id: xss83sOY
|
||||
modrinth-featured: false
|
||||
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publishHangar:
|
||||
needs: [buildJars]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up JDK 1.16
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '16'
|
||||
- name: Download Release Assets
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: asl-files
|
||||
- name: Move HangarUploader.jar
|
||||
working-directory: hangar-uploader/target/
|
||||
run: mv HangarUploader.jar ../../
|
||||
- name: Execute HangarUploader.jar
|
||||
run: java -jar HangarUploader.jar ${{ secrets.HANGAR_TOKEN }} ${{ needs.buildJars.outputs.plugin_release }} ${{ github.event.release.prerelease }}
|
||||
env:
|
||||
GITHUB_RELEASE_BODY: ${{ github.event.release.body }}
|
||||
@@ -2,3 +2,4 @@
|
||||
/.idea/
|
||||
/**/dependency-reduced-pom.xml
|
||||
/**/target/
|
||||
/**/__pycache__/
|
||||
@@ -0,0 +1,64 @@
|
||||
when:
|
||||
- event: pull_request
|
||||
path:
|
||||
include: [".woodpecker/pr_preview.yml", "docs/**", "mkdocs.yml", "requirements.txt"]
|
||||
|
||||
variables:
|
||||
setup_git: &setup_git
|
||||
- apk add git
|
||||
- git config --global user.email "andre_601-bot@noreply.codeberg.org"
|
||||
- git config --global user.name "PR Docs Preview Builder"
|
||||
|
||||
steps:
|
||||
- name: "Clone Preview Repository"
|
||||
image: alpine:3.20.3
|
||||
commands:
|
||||
- chmod -R a+w .
|
||||
- <<: *setup_git
|
||||
- git clone https://codeberg.org/Andre601/preview.git site
|
||||
- chmod -R a+w site
|
||||
- cd site
|
||||
- git switch ${CI_REPO_NAME,,}/${CI_COMMIT_PULL_REQUEST} 2>/dev/null || git switch -c ${CI_REPO_NAME,,}/${CI_COMMIT_PULL_REQUEST}
|
||||
- name: "Build Docs"
|
||||
image: woodpeckerci/plugin-mkdocs:latest
|
||||
settings:
|
||||
pip_install_file: requirements.txt
|
||||
- name: "Commit and Push"
|
||||
image: alpine:3.20.3
|
||||
secrets:
|
||||
- bot_token
|
||||
commands:
|
||||
- <<: *setup_git
|
||||
- mv preview-domains site/.domains
|
||||
- cd site
|
||||
- git remote add upstream https://$BOT_TOKEN@codeberg.org/Andre601/preview.git
|
||||
- git add --all
|
||||
- git commit -m "Update Preview for ${CI_REPO_NAME}/${CI_COMMIT_PULL_REQUEST} ($( env TZ=Europe/Zurich date +"%d.%m.%Y %Z" )) [SKIP CI]"
|
||||
- git push --set-upstream upstream ${CI_REPO_NAME,,}/${CI_COMMIT_PULL_REQUEST} -f
|
||||
- name: "Comment on Success"
|
||||
image: mcs94/gitea-comment
|
||||
when:
|
||||
- status: ["success"]
|
||||
settings:
|
||||
gitea_address: https://codeberg.org
|
||||
gitea_token:
|
||||
from_secret: BOT_TOKEN
|
||||
comment: |-
|
||||
Pull request Preview Ready!
|
||||
|
||||
| Name | Value |
|
||||
|--------|--------------------------------------------------------------------------|
|
||||
| Commit | ${CI_COMMIT_SHA} |
|
||||
| Link | https://preview.andre601.ch/@${CI_REPO_NAME,,}~${CI_COMMIT_PULL_REQUEST} |
|
||||
- name: "Comment on Failure"
|
||||
image: mcs94/gitea-comment
|
||||
when:
|
||||
- status: ["failure"]
|
||||
settings:
|
||||
gitea_address: https://codeberg.org
|
||||
gitea_token:
|
||||
from_secret: BOT_TOKEN
|
||||
comment: |-
|
||||
Pull request Preview **failed**!
|
||||
|
||||
Please check the CI logs for details: ${CI_PIPELINE_URL}
|
||||
@@ -0,0 +1,46 @@
|
||||
when:
|
||||
- event: tag
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
|
||||
steps:
|
||||
- name: "Build Jar and Upload"
|
||||
image: maven:3.9.9-eclipse-temurin-21
|
||||
secrets:
|
||||
- modrinth_api_token
|
||||
- hangar_api_token
|
||||
- discord_webhook_url
|
||||
environment:
|
||||
CODEBERG_API_TOKEN:
|
||||
from_secret: cb_token
|
||||
commands:
|
||||
- mvn clean install
|
||||
- cd version-uploader/target
|
||||
- mv VersionUploader.jar ../../
|
||||
- cd ../../
|
||||
- java -jar VersionUploader.jar --all
|
||||
- name: "Upload Jars to Codeber"
|
||||
image: woodpeckerci/plugin-gitea-release
|
||||
settings:
|
||||
base_url: https://codeberg.org
|
||||
files:
|
||||
- "bungeecord/target/AdvancedServerList-*.jar"
|
||||
- "paper/target/AdvancedServerList-*.jar"
|
||||
- "velocity/target/AdvancedServerList-*.jar"
|
||||
- "addons/banplugins/target/AdvancedServerList-*.jar"
|
||||
api_key:
|
||||
from_secret: cb_token
|
||||
- name: "Create Post on Mastodon"
|
||||
image: woodpeckerci/plugin-mastodon-post
|
||||
when:
|
||||
- status: ["success"]
|
||||
settings:
|
||||
server_url: https://swiss.social
|
||||
access_token:
|
||||
from_secret: mastodon_token
|
||||
status: |-
|
||||
#AdvancedServerList was updated to ${CI_COMMIT_TAG} and can be downloaded from #Modrinth, #Hangar and #Codeberg!
|
||||
|
||||
Modrinth - https://modrinth.com/plugin/advancedserverlist
|
||||
Hangar - https://hangar.papermc.io/Andre_601/AdvancedServerList
|
||||
Codeberg - https://codeberg.org/Andre601/AdvancedServerList
|
||||
visibility: public
|
||||
@@ -0,0 +1,36 @@
|
||||
when:
|
||||
- event: push
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
path:
|
||||
include: [".woodpecker/publish_docs.yml", "docs/**", "mkdocs.yml", "requirements.txt"]
|
||||
|
||||
variables:
|
||||
setup_git: &setup_git
|
||||
- apk add git
|
||||
- git config --global user.email "andre_601-bot@noreply.codeberg.org"
|
||||
- git config --global user.name "ASL Docs Builder"
|
||||
|
||||
steps:
|
||||
- name: "Clone target Repository"
|
||||
image: alpine:3.20.3
|
||||
commands:
|
||||
- chmod -R a+w .
|
||||
- <<: *setup_git
|
||||
- git clone -b pages https://codeberg.org/Andre601/asl-docs.git site
|
||||
- chmod -R a+w site
|
||||
- name: "Build Docs"
|
||||
image: woodpeckerci/plugin-mkdocs:latest
|
||||
settings:
|
||||
pip_install_file: requirements.txt
|
||||
- name: "Commit and Push"
|
||||
image: alpine:3.20.3
|
||||
secrets:
|
||||
- bot_token
|
||||
commands:
|
||||
- <<: *setup_git
|
||||
- mv domains site/.domains
|
||||
- cd site
|
||||
- git remote set-url origin https://$BOT_TOKEN@codeberg.org/Andre601/asl-docs.git
|
||||
- git add --all
|
||||
- git commit -m "Update AdvancedServerList Docs ($( env TZ=Europe/Zurich date +"%d.%m.%Y %Z" )) [SKIP CI]"
|
||||
- git push
|
||||
@@ -1,116 +1,113 @@
|
||||
# AdvancedServerList
|
||||
|
||||
AdvancedServerList is a plugin that allowys you to customize your server's MOTD, favicon, Player count text and player count hover displayed in a player's multiplayer server list based on conditions and priorities.
|
||||
AdvancedServerList is a plugin designed to allow the largest possible customization for your server's appearance in a Player's multiplayer screen.
|
||||
The plugin offers extensive customization including changing the MOTD, player count text, player count hover and favicon of your server. All while also allowing you to create multiple profiles with priorities and conditions to set when a specific profile should be displayed.
|
||||
|
||||
## Downloads
|
||||
|
||||
<a href="https://modrinth.com/plugin/advancedserverlist" target="_blank">
|
||||
<img src="https://cdn.jsdelivr.net/gh/Andre601/devins-badges@13e0142/assets/compact/available/modrinth_vector.svg" height="46" alt="modrinth" title="Available on Modrinth">
|
||||
<img src="https://cdn.jsdelivr.net/gh/Andre601/devins-badges@13e0142/assets/compact/available/modrinth_vector.svg" height="48" alt="modrinth" title="Available on Modrinth">
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://hangar.papermc.io/Andre_601/AdvancedServerList" target="_blank">
|
||||
<img src="https://cdn.jsdelivr.net/gh/Andre601/devins-badges@13e0142/assets/compact/available/hangar_vector.svg" height="46" alt="hangar" title="Available on Hangar">
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://www.spigotmc.org/resources/102910" target="_blank">
|
||||
<img src="https://cdn.jsdelivr.net/gh/Andre601/devins-badges@b9ff12b/assets/compact/available/spigot_vector.svg" height="46" alt="spigot" title="Available on SpigotMC">
|
||||
<img src="https://cdn.jsdelivr.net/gh/Andre601/devins-badges@13e0142/assets/compact/available/hangar_vector.svg" height="48" alt="hangar" title="Available on Hangar">
|
||||
</a>
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
<a href="https://www.spigotmc.org" target="_blank">
|
||||
<img src="https://cdn.jsdelivr.net/gh/Andre601/devins-badges@13e0142/assets/compact-minimal/supported/spigot_vector.svg" height="64" alt="platform-spigot" title="Tested on Spigot">
|
||||
</a>
|
||||
<a href="https://papermc.io" target="_blank">
|
||||
<img src="https://cdn.jsdelivr.net/gh/Andre601/devins-badges@13e0142/assets/compact-minimal/supported/paper_vector.svg" height="64" alt="platform-paper" title="Tested on Paper">
|
||||
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/supported/paper_vector.svg" height="48" alt="platform-paper" title="Tested on Paper">
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://www.spigotmc.org" target="_blank">
|
||||
<img src="https://cdn.jsdelivr.net/gh/Andre601/devins-badges@13e0142/assets/compact-minimal/supported/bungeecord_vector.svg" height="64" alt="platform-bungeecord" title="Tested on BungeeCord">
|
||||
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/supported/bungeecord_vector.svg" height="48" alt="platform-bungeecord" title="Tested on BungeeCord">
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://www.papermc.io" target="_blank">
|
||||
<img src="https://cdn.jsdelivr.net/gh/Andre601/devins-badges@13e0142/assets/compact-minimal/supported/waterfall_vector.svg" height="64" alt="platform-waterfall" title="Tested on Waterfall">
|
||||
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/supported/waterfall_vector.svg" height="48" alt="platform-waterfall" title="Tested on Waterfall">
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://velocitypowered.com" target="_blank">
|
||||
<img src="https://cdn.jsdelivr.net/gh/Andre601/devins-badges@13e0142/assets/compact-minimal/supported/velocity_vector.svg" height="64" alt="platform-velocity" title="Tested on Velocity">
|
||||
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/supported/velocity_vector.svg" height="48" alt="platform-velocity" title="Tested on Velocity">
|
||||
</a>
|
||||
|
||||
## What is a Server List Profile?
|
||||
A Server List Profile refers to a YAML file located in the plugin's `profiles` directory.
|
||||
It allows you to configure specific aspects of the server display in the MC-Client's multiplayer server list, such as the MOTD, player count text, player count hover or Favicon.
|
||||
## Server List Profiles
|
||||
|
||||
> [*Read More*][profiles]
|
||||
A Server List Profile is a single YAML file located in the plugin's `profiles` folder. It contains settings that allow you to modify the server's appearance within a Player's multiplayer screen.
|
||||
|
||||
### Conditions
|
||||
Thanks to conditions can you determine when a profile should be displayed in the player’s server list, to allow displaying specific text (i.e. a warning when they are using an outdated version).
|
||||
### Priority and Conditions
|
||||
|
||||
When multiple profiles exist that have valid conditions will the one with the highest priority (Higher number = higher priority) be selected. Having no conditions makes the profile automatically return true.
|
||||
Each file has a priority and optional condition. The priority is used to determine what file AdvancedServerList should use first - starting from highest number to lowest - with conditions being used to set when the profile should be used.
|
||||
Once a profile has been found with a condition returning true - which is the default should there be no condition - will AdvancedServerList use said profile.
|
||||
|
||||
> [*Read More*][conditions]
|
||||
> *Read more about [Priority](https://asl.andre601.ch/profiles/#priority) and [Conditions](https://asl.andre601.ch/profiles/#condition)*
|
||||
|
||||
### Placeholders
|
||||
Placeholders can be used within conditions to further customize them. They can also be used in any other text option (motd, playerCount and players) to display their respective values.
|
||||
The format is `${<target> <identifier>}` which was adopted from BungeeTabListPlus. Depending on what platform you use the plugin on are only specific placeholders available.
|
||||
|
||||
When using the SpigotMC/PaperMC version can you also use placeholders from [PlaceholderAPI][placeholderapi] using its `%<identifier>_<values>%` placeholder format. PlaceholderAPI needs to be installed for that.
|
||||
AdvancedServerList supports two sets of placeholders. The first one being its own placeholders using the `${<type> <values>}` format adobted from BungeeTabListPlus and the second one being placeholders from PlaceholderAPI using its `%<identifier>_<values>%` placeholder format.
|
||||
|
||||
If you're a plugin dev can you even add your own custom placeholders through AdvancedServerList's own API.
|
||||
Note that internal placeholders can be used in all text-based options of a profile, while PlaceholderAPI placeholders can be used in all text-based options except the `condition` one.
|
||||
As a final note can plugins provide their own placeholders to use within AdvancedServerList by using its API.
|
||||
|
||||
> [*Read More*][placeholders]
|
||||
> *Read more about [Placeholders](https://asl.andre601.ch/profiles/placeholders/)*
|
||||
|
||||
### Formatting
|
||||
The plugin uses the MiniMessage format for a consistent, easy way of formatting your text.
|
||||
Only certain options are usable for each option. As an example does motd support HEX colours and gradients, while the other options only support default colour codes.
|
||||
|
||||
> [*Read More*][minimessage]
|
||||
AdvancedServerList utilizes the MiniMessage formatting to handle colors and formatting of text. This option was chosen over legacy formatting - using `&` character - for being more clear and readable in its design, not to mention having an easier way for more complex features such as RGB gradients.
|
||||
|
||||
> *Read more about [Formatting](https://asl.andre601.ch/profiles/formatting/)*
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Dependency | Platforms | Required? |
|
||||
|------------------|---------------|-----------|
|
||||
| [ProtocolLib] | Spigot | Yes |
|
||||
| [PlaceholderAPI] | Spigot, Paper | No |
|
||||
| [ViaVersion] | Spigot, Paper | No |
|
||||
AdvancedServerList soft-depends on the following plugins, meaning their inclusion is not required.
|
||||
|
||||
| Plugin | Supported Platforms |
|
||||
|-------------------------------------------------------------------------|----------------------------------------|
|
||||
| [PlaceholderAPI](https://hangar.papermc.io/HelpChat/PlaceholderAPI) | Paper |
|
||||
| [ViaVersion](https://hangar.papermc.io/ViaVersion/ViaVersion) | Paper |
|
||||
| [PAPIProxyBridge](https://hangar.papermc.io/William278/PAPIProxyBridge) | BungeeCord, Waterfall, Velocity |
|
||||
| [Maintenance](https://hangar.papermc.io/kennytv/Maintenance) | Paper, BungeeCord, Waterfall, Velocity |
|
||||
|
||||
## Statistics
|
||||
|
||||
This plugin sends statistics to [bStats] to display.
|
||||
You can disable this in the global bStats config file located in `/plugins/bstats/`
|
||||
The plugin sends statistics to [bStats](https://bstats.org) to show.
|
||||
You can disable the sending of statistics in the global bStats config located inside `/plugins/bstats/`.
|
||||
|
||||
- [BungeeCord][bstats-bungee]
|
||||
- [Paper/Spigot][bstats-spigot]
|
||||
- [Velocity][bstats-velocity]
|
||||
You can find the bStats pages of the plugin for the following platforms:
|
||||
|
||||
- [Paper](https://bstats.org/plugin/bungeecord/AdvancedServerList/15584)
|
||||
- [BungeeCord, Waterfall](https://bstats.org/plugin/bungeecord/AdvancedServerList/15585)
|
||||
- [Velocity](https://bstats.org/plugin/velocity/AdvancedServerList/15587)
|
||||
|
||||
## Videos
|
||||
|
||||
Below can you find a collection of videos made for my plugin. If you made a video and it isn't shown here, let me know and I'll add it.
|
||||
|
||||
<details><summary>Click to show/hide Videos</summary>
|
||||
<p>
|
||||
|
||||
Spanish Video by Ajneb97
|
||||
[](https://youtube.com/watch?v=rIbljm_4HVI)
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
## Screenshots
|
||||
|
||||
<a href="https://modrinth.com/plugin/advancedserverlist/gallery" target="_blank">
|
||||
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/cozy/documentation/modrinth-gallery_vector.svg" height="64" alt="gallery" title="Check out the Gallery">
|
||||
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/documentation/modrinth-gallery_vector.svg" height="48" alt="gallery" title="Check out the Gallery">
|
||||
</a>
|
||||
|
||||
## Support
|
||||
<a href="https://discord.gg/6dazXp6" target="_blank">
|
||||
<img src="https://cdn.jsdelivr.net/gh/Andre601/devins-badges@13e0142/assets/compact-minimal/social/discord-singular_vector.svg" height="64" alt="discord" title="Join my Discord Server">
|
||||
|
||||
<a href="https://discord.gg/MyPTjpsgGH" target="_blank">
|
||||
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/social/discord-singular_vector.svg" height="48" alt="discord" title="Join the M.O.S.S. Discord Server">
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://app.revolt.chat/invite/74TpERXA" target="_blank">
|
||||
<img src="https://cdn.jsdelivr.net/gh/Andre601/devins-badges@13e0142/assets/compact-minimal/social/revolt-singular_vector.svg" height="64" alt="revolt" title="Join my Revolt Server">
|
||||
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/social/revolt-singular_vector.svg" height="48" alt="revolt" title="Join my Revolt Server">
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://blobfox.coffee/@andre_601" target="_blank">
|
||||
<img src="https://cdn.jsdelivr.net/gh/Andre601/devins-badges@13e0142/assets/compact-minimal/social/mastodon-singular_vector.svg" height="64" alt="mastodon" title="Chat with me on Mastodon">
|
||||
</a>
|
||||
|
||||
<!-- Links -->
|
||||
[profiles]: https://www.andre601.ch/AdvancedServerList/profiles/
|
||||
[conditions]: https://www.andre601.ch/AdvancedServerList/profiles/#conditions
|
||||
[placeholders]: https://www.andre601.ch/AdvancedServerList/profiles/placeholders/
|
||||
[minimessage]: https://www.andre601.ch/AdvancedServerList/profiles/formatting/
|
||||
|
||||
[modrinth]: https://modrinth.com/plugin/advancedserverlist
|
||||
|
||||
[protocollib]: https://www.spigotmc.org/resources/1997/
|
||||
[placeholderapi]: https://www.spigotmc.org/resources/6245/
|
||||
[viaversion]: https://www.spigotmc.org/resources/19254/
|
||||
|
||||
[bstats]: https://bstats.org
|
||||
[bstats-bungee]: https://bstats.org/plugin/bungeecord/AdvancedServerList/15585
|
||||
[bstats-spigot]: https://bstats.org/plugin/bukkit/AdvancedServerList/15584
|
||||
[bstats-velocity]: https://bstats.org/plugin/velocity/AdvancedServerList/15587
|
||||
|
||||
[gallery]: https://modrinth.com/mod/advancedserverlist/gallery
|
||||
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@3/assets/compact/social/mastodon-singular_vector.svg" height="48" alt="revolt" title="Chat with me on Mastodon">
|
||||
</a>
|
||||
@@ -0,0 +1,58 @@
|
||||
# Supported Versions
|
||||
|
||||
To ensure proper safety and support are only certain versions and Platforms of AdvancedServerList maintained and supported.
|
||||
|
||||
## Supported Plugin Versions
|
||||
|
||||
| Major Version | Supported |
|
||||
|---------------|-----------|
|
||||
| v4 | Yes |
|
||||
| v3 | Yes* |
|
||||
| v2 | No |
|
||||
| v1 | No |
|
||||
|
||||
\*Only receives support but no updates or bug fixes.
|
||||
|
||||
## Supported Server/Proxy Versions
|
||||
|
||||
| Server/Proxy Version* | Supported |
|
||||
|-----------------------|-----------|
|
||||
| 1.21.x | Yes |
|
||||
| 1.20.x | Yes |
|
||||
| 1.19.x | No |
|
||||
| 1.18.x | No |
|
||||
| 1.17.x | No |
|
||||
| 1.16.x | No |
|
||||
| 1.15.x | No |
|
||||
| 1.14.x | No |
|
||||
| 1.13.x | No |
|
||||
| 1.12.x | No |
|
||||
| 1.11.x | No |
|
||||
| 1.10.x | No |
|
||||
| 1.19.x | No |
|
||||
| 1.8.x | No |
|
||||
|
||||
\*Only the latest patch version is actively supported should "Supported" have "Yes".
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
### Servers
|
||||
|
||||
| Server | Supported |
|
||||
|--------|-----------|
|
||||
| Spigot | No |
|
||||
| Paper | Yes |
|
||||
| Purpur | Yes* |
|
||||
| Mohist | No |
|
||||
|
||||
\*Support hasn't actively been tested, but should work without issues.
|
||||
|
||||
### Proxies
|
||||
|
||||
| Proxy | Supported |
|
||||
|------------|-----------|
|
||||
| BungeeCord | Yes** |
|
||||
| Waterfall | Yes** |
|
||||
| Velocity | Yes |
|
||||
|
||||
\*\*Platform version of this plugin is maintenance only, not receiving any bug fixes for major breaking changes anymore.
|
||||
@@ -0,0 +1,135 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ MIT License
|
||||
~
|
||||
~ Copyright (c) 2022-2024 Andre_601
|
||||
~
|
||||
~ Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
~ of this software and associated documentation files (the "Software"), to deal
|
||||
~ in the Software without restriction, including without limitation the rights
|
||||
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
~ copies of the Software, and to permit persons to whom the Software is
|
||||
~ furnished to do so, subject to the following conditions:
|
||||
~
|
||||
~ The above copyright notice and this permission notice shall be included in all
|
||||
~ copies or substantial portions of the Software.
|
||||
~
|
||||
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
~ SOFTWARE.
|
||||
~
|
||||
-->
|
||||
|
||||
<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">
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>ch.andre601.advancedserverlist</groupId>
|
||||
<version>parent</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>banplugins</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>${plugin.version}</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>16</maven.compiler.source>
|
||||
<maven.compiler.target>16</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>sonatype</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>papermc</id>
|
||||
<url>https://repo.papermc.io/repository/maven-public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>libertybans</id>
|
||||
<url>https://mvn-repo.arim.space/affero-gpl3/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<!-- Proxy/Server -->
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.20-R0.2</version>
|
||||
<type>jar</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.20-R0.2</version>
|
||||
<type>javadoc</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.velocitypowered</groupId>
|
||||
<artifactId>velocity-api</artifactId>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Plugin APIs -->
|
||||
<dependency>
|
||||
<groupId>com.github.DevLeoko</groupId>
|
||||
<artifactId>AdvancedBan</artifactId>
|
||||
<version>v2.3.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>space.arim.libertybans</groupId>
|
||||
<artifactId>bans-api</artifactId>
|
||||
<version>1.1.0-RC1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.gitlab.ruany</groupId>
|
||||
<artifactId>LiteBansAPI</artifactId>
|
||||
<version>0.5.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.andre601.asl-api</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>${api.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>AdvancedServerList-BanPlugins-Addon-${plugin.version}</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<filtering>true</filtering>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.banplugins;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
|
||||
import ch.andre601.advancedserverlist.banplugins.placeholders.AdvancedBanPlaceholders;
|
||||
import ch.andre601.advancedserverlist.banplugins.placeholders.LibertyBansPlaceholders;
|
||||
import ch.andre601.advancedserverlist.banplugins.placeholders.LiteBansPlaceholders;
|
||||
|
||||
public enum BanPluginsList{
|
||||
|
||||
ADVANCED_BAN("AdvancedBan", new AdvancedBanPlaceholders(), true, true, false),
|
||||
LIBERTY_BANS("LibertyBans", new LibertyBansPlaceholders(), true, true, true),
|
||||
LITE_BANS("LiteBans", new LiteBansPlaceholders(), true, true, true);
|
||||
|
||||
private final String name;
|
||||
private final PlaceholderProvider placeholderProvider;
|
||||
private final boolean paper, bungeecord, velocity;
|
||||
|
||||
BanPluginsList(String name, PlaceholderProvider placeholderProvider, boolean paper, boolean bungeecord, boolean velocity){
|
||||
this.name = name;
|
||||
this.placeholderProvider = placeholderProvider;
|
||||
this.paper = paper;
|
||||
this.bungeecord = bungeecord;
|
||||
this.velocity = velocity;
|
||||
}
|
||||
|
||||
public String getName(){
|
||||
return name;
|
||||
}
|
||||
|
||||
public PlaceholderProvider getPlaceholderProvider(){
|
||||
return placeholderProvider;
|
||||
}
|
||||
|
||||
public boolean supportsPaper(){
|
||||
return paper;
|
||||
}
|
||||
|
||||
public boolean supportsBungeeCord(){
|
||||
return bungeecord;
|
||||
}
|
||||
|
||||
public boolean supportsVelocity(){
|
||||
return velocity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.banplugins.bungeecord;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.AdvancedServerListAPI;
|
||||
import ch.andre601.advancedserverlist.banplugins.BanPluginsList;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
|
||||
public class BungeeCordBanPluginsAddon extends Plugin{
|
||||
|
||||
@Override
|
||||
public void onEnable(){
|
||||
if(getProxy().getPluginManager().getPlugin("AdvancedServerList") == null){
|
||||
getLogger().warning("AdvancedServerList is not enabled. This plugin requires it to function!");
|
||||
return;
|
||||
}
|
||||
|
||||
int loadedPlaceholders = loadPlaceholderProviders();
|
||||
if(loadedPlaceholders == 0){
|
||||
getLogger().warning("No compatible Ban plugin was found to register placeholders for.");
|
||||
}else{
|
||||
getLogger().info("Loaded " + loadedPlaceholders + " Placeholder Set(s) for AdvancedServerList!");
|
||||
}
|
||||
}
|
||||
|
||||
private int loadPlaceholderProviders(){
|
||||
int loaded = 0;
|
||||
AdvancedServerListAPI api = AdvancedServerListAPI.get();
|
||||
|
||||
for(BanPluginsList entry : BanPluginsList.values()){
|
||||
if(!entry.supportsBungeeCord())
|
||||
continue;
|
||||
|
||||
if(getProxy().getPluginManager().getPlugin(entry.getName()) != null){
|
||||
getLogger().info("Registering Placeholders for " + entry.getName() + "...");
|
||||
api.addPlaceholderProvider(entry.getPlaceholderProvider());
|
||||
getLogger().info("Placeholders successfully registered!");
|
||||
|
||||
loaded++;
|
||||
}
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.banplugins.paper;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.AdvancedServerListAPI;
|
||||
import ch.andre601.advancedserverlist.banplugins.BanPluginsList;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class PaperBanPluginsAddon extends JavaPlugin{
|
||||
|
||||
@Override
|
||||
public void onEnable(){
|
||||
if(!getServer().getPluginManager().isPluginEnabled("AdvancedServerList")){
|
||||
getLogger().warning("AdvancedServerList is not enabled. This plugin requires it to function!");
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
|
||||
int loadedPlaceholders = loadPlaceholderProviders();
|
||||
if(loadedPlaceholders == 0){
|
||||
getLogger().warning("No compatible Ban plugin was found to register placeholders for. Disabling plugin...");
|
||||
|
||||
getServer().getPluginManager().disablePlugin(this);
|
||||
}else{
|
||||
getLogger().info("Loaded " + loadedPlaceholders + " Placeholder Set(s) for AdvancedServerList!");
|
||||
}
|
||||
}
|
||||
|
||||
private int loadPlaceholderProviders(){
|
||||
int loaded = 0;
|
||||
AdvancedServerListAPI api = AdvancedServerListAPI.get();
|
||||
|
||||
for(BanPluginsList entry : BanPluginsList.values()){
|
||||
if(!entry.supportsPaper())
|
||||
continue;
|
||||
|
||||
if(getServer().getPluginManager().isPluginEnabled(entry.getName())){
|
||||
getLogger().info("Registering Placeholders for " + entry.getName() + "...");
|
||||
api.addPlaceholderProvider(entry.getPlaceholderProvider());
|
||||
getLogger().info("Placeholders successfully registered!");
|
||||
|
||||
loaded++;
|
||||
}
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.banplugins.placeholders;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.banplugins.providers.AdvancedBanProvider;
|
||||
|
||||
public class AdvancedBanPlaceholders extends PlaceholderProvider{
|
||||
|
||||
private AdvancedBanProvider provider = null;
|
||||
|
||||
public AdvancedBanPlaceholders(){
|
||||
super("advancedban");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
|
||||
if(provider == null)
|
||||
provider = new AdvancedBanProvider();
|
||||
|
||||
String[] args = placeholder.split("\\s", 2);
|
||||
|
||||
return switch(args[0]) {
|
||||
// Mute-related placeholders
|
||||
case "isMuted" -> provider.muted(player);
|
||||
case "muteReason" -> provider.muteReason(player);
|
||||
case "muteDuration" -> {
|
||||
if(args.length == 1)
|
||||
yield provider.muteDuration(player);
|
||||
|
||||
boolean fromStart = Boolean.getBoolean(args[1]);
|
||||
yield provider.muteDuration(player, fromStart);
|
||||
}
|
||||
case "muteExpiration" -> {
|
||||
if(args.length == 1)
|
||||
yield provider.muteExpirationDate(player);
|
||||
|
||||
yield provider.banExpirationDate(player, args[1]);
|
||||
}
|
||||
|
||||
// Ban-related placeholders
|
||||
case "isBanned" -> provider.banned(player);
|
||||
case "banReason" -> provider.banReason(player);
|
||||
case "banDuration" -> {
|
||||
if(args.length == 1)
|
||||
yield provider.banDuration(player);
|
||||
|
||||
boolean fromStart = Boolean.getBoolean(args[1]);
|
||||
yield provider.banDuration(player, fromStart);
|
||||
}
|
||||
case "banExpiration" -> {
|
||||
if(args.length == 1)
|
||||
yield provider.banExpirationDate(player);
|
||||
|
||||
yield provider.banExpirationDate(player, args[1]);
|
||||
}
|
||||
|
||||
// Unknown/Invalid placeholder.
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,40 +23,51 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.bukkit.objects.placeholders;
|
||||
package ch.andre601.advancedserverlist.banplugins.placeholders;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
|
||||
import ch.andre601.advancedserverlist.api.bukkit.objects.BukkitPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.banplugins.providers.LibertyBansProvider;
|
||||
|
||||
public class BukkitPlayerPlaceholders extends PlaceholderProvider{
|
||||
public class LibertyBansPlaceholders extends PlaceholderProvider{
|
||||
|
||||
private BukkitPlayerPlaceholders(){
|
||||
super("player");
|
||||
}
|
||||
private LibertyBansProvider provider = null;
|
||||
|
||||
public static BukkitPlayerPlaceholders init(){
|
||||
return new BukkitPlayerPlaceholders();
|
||||
public LibertyBansPlaceholders(){
|
||||
super("libertybans");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
|
||||
if(!(player instanceof BukkitPlayer bukkitPlayer))
|
||||
return null;
|
||||
if(provider == null)
|
||||
provider = LibertyBansProvider.create();
|
||||
|
||||
return switch(placeholder){
|
||||
case "name" -> bukkitPlayer.getName();
|
||||
case "protocol" -> String.valueOf(bukkitPlayer.getProtocol());
|
||||
case "uuid" -> bukkitPlayer.getUUID().toString();
|
||||
case "hasPlayedBefore" -> returnValue(bukkitPlayer, bukkitPlayer.hasPlayedBefore());
|
||||
case "isBanned" -> returnValue(bukkitPlayer, bukkitPlayer.isBanned());
|
||||
case "isWhitelisted" -> returnValue(bukkitPlayer, bukkitPlayer.isWhitelisted());
|
||||
String[] args = placeholder.split("\\s", 2);
|
||||
|
||||
return switch(args[0]){
|
||||
// Mute-related placeholders
|
||||
case "isMuted" -> provider.muted(player);
|
||||
case "muteReason" -> provider.muteReason(player);
|
||||
case "muteExpiration" -> {
|
||||
if(args.length == 1)
|
||||
yield provider.muteExpirationDate(player);
|
||||
|
||||
yield provider.muteExpirationDate(player, args[1]);
|
||||
}
|
||||
|
||||
// Ban-related placeholders
|
||||
case "isBanned" -> provider.banned(player);
|
||||
case "banReason" -> provider.banReason(player);
|
||||
case "banExpiration" -> {
|
||||
if(args.length == 1)
|
||||
yield provider.banExpirationDate(player);
|
||||
|
||||
yield provider.banExpirationDate(player, args[1]);
|
||||
}
|
||||
|
||||
// Unknown/Invalid placeholder
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private String returnValue(BukkitPlayer player, Object value){
|
||||
return player.getPlayer() == null ? null : String.valueOf(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.banplugins.placeholders;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.banplugins.providers.LiteBansProvider;
|
||||
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
|
||||
public class LiteBansPlaceholders extends PlaceholderProvider{
|
||||
|
||||
private LiteBansProvider provider = null;
|
||||
|
||||
public LiteBansPlaceholders(){
|
||||
super("litebans");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
|
||||
if(provider == null)
|
||||
provider = new LiteBansProvider();
|
||||
|
||||
String[] args = placeholder.split("\\s", 2);
|
||||
|
||||
CompletableFuture<String> future = CompletableFuture.supplyAsync(() ->
|
||||
switch(args[0]){
|
||||
// Mute-related placeholders
|
||||
case "isMuted" -> provider.muted(player);
|
||||
case "muteReason" -> provider.muteReason(player);
|
||||
case "muteDuration" -> provider.muteDuration(player);
|
||||
case "muteExpiration" -> {
|
||||
if(args.length == 1)
|
||||
yield provider.muteExpirationDate(player);
|
||||
|
||||
yield provider.muteExpirationDate(player, args[1]);
|
||||
}
|
||||
|
||||
// Ban-related placeholders
|
||||
case "isBanned" -> provider.banned(player);
|
||||
case "banReason" -> provider.banReason(player);
|
||||
case "banDuration" -> provider.banDuration(player);
|
||||
case "banExpiration" -> {
|
||||
if(args.length == 1)
|
||||
yield provider.banExpirationDate(player);
|
||||
|
||||
yield provider.banExpirationDate(player, args[1]);
|
||||
}
|
||||
|
||||
//Unknown/Invalid placeholder
|
||||
default -> null;
|
||||
}
|
||||
);
|
||||
|
||||
try{
|
||||
return future.join();
|
||||
}catch(CancellationException | CompletionException ex){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.banplugins.providers;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.primitives.UnsignedLongs;
|
||||
import me.leoko.advancedban.manager.PunishmentManager;
|
||||
import me.leoko.advancedban.utils.Punishment;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
public class AdvancedBanProvider implements PunishmentProvider{
|
||||
|
||||
private static PunishmentManager getAPI(){
|
||||
return PunishmentManager.get();
|
||||
}
|
||||
|
||||
private static String noDashesUUID(UUID uuid){
|
||||
return hexString(uuid.getMostSignificantBits()) + hexString(uuid.getLeastSignificantBits());
|
||||
}
|
||||
|
||||
private static Punishment mute(GenericPlayer player){
|
||||
return getAPI().getMute(noDashesUUID(player.getUUID()));
|
||||
}
|
||||
|
||||
private static Punishment ban(GenericPlayer player){
|
||||
return getAPI().getBan(noDashesUUID(player.getUUID()));
|
||||
}
|
||||
|
||||
private static String hexString(long unsigned){
|
||||
return Strings.padStart(UnsignedLongs.toString(unsigned, 16), 16, '0');
|
||||
}
|
||||
|
||||
@Override
|
||||
public String muted(GenericPlayer player){
|
||||
return String.valueOf(getAPI().isMuted(noDashesUUID(player.getUUID())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String banned(GenericPlayer player){
|
||||
return String.valueOf(getAPI().isBanned(noDashesUUID(player.getUUID())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String muteReason(GenericPlayer player){
|
||||
Punishment mute = mute(player);
|
||||
if(mute == null)
|
||||
return null;
|
||||
|
||||
return mute.getReason();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String banReason(GenericPlayer player){
|
||||
Punishment ban = ban(player);
|
||||
if(ban == null)
|
||||
return null;
|
||||
|
||||
return ban.getReason();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String muteDuration(GenericPlayer player){
|
||||
return muteDuration(player, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String banDuration(GenericPlayer player){
|
||||
return banDuration(player, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String muteExpirationDate(GenericPlayer player, String pattern){
|
||||
Punishment mute = mute(player);
|
||||
if(mute == null)
|
||||
return null;
|
||||
|
||||
long end = mute.getEnd();
|
||||
if(end < 0L)
|
||||
return "never";
|
||||
|
||||
return this.returnDate(pattern, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String banExpirationDate(GenericPlayer player, String pattern){
|
||||
Punishment ban = ban(player);
|
||||
if(ban == null)
|
||||
return null;
|
||||
|
||||
long end = ban.getEnd();
|
||||
if(end < 0L)
|
||||
return "never";
|
||||
|
||||
return this.returnDate(pattern, end);
|
||||
}
|
||||
|
||||
public String muteDuration(GenericPlayer player, boolean fromStart){
|
||||
Punishment mute = mute(player);
|
||||
if(mute == null)
|
||||
return null;
|
||||
|
||||
return mute.getDuration(fromStart);
|
||||
}
|
||||
|
||||
public String banDuration(GenericPlayer player, boolean fromStart){
|
||||
Punishment ban = ban(player);
|
||||
if(ban == null)
|
||||
return null;
|
||||
|
||||
return ban.getDuration(fromStart);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.banplugins.providers;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import space.arim.libertybans.api.LibertyBans;
|
||||
import space.arim.libertybans.api.PunishmentType;
|
||||
import space.arim.libertybans.api.punish.Punishment;
|
||||
import space.arim.libertybans.api.user.KnownAccount;
|
||||
import space.arim.omnibus.Omnibus;
|
||||
import space.arim.omnibus.OmnibusProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class LibertyBansProvider implements PunishmentProvider{
|
||||
private final LibertyBans libertyBans;
|
||||
|
||||
private LibertyBansProvider(LibertyBans libertyBans){
|
||||
this.libertyBans = libertyBans;
|
||||
}
|
||||
|
||||
public static LibertyBansProvider create(){
|
||||
Omnibus omnibus = OmnibusProvider.getOmnibus();
|
||||
LibertyBans libertyBans = omnibus.getRegistry().getProvider(LibertyBans.class).orElseThrow();
|
||||
|
||||
return new LibertyBansProvider(libertyBans);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String muted(GenericPlayer player){
|
||||
return String.valueOf(punishment(player.getUUID(), PunishmentType.MUTE) != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String banned(GenericPlayer player){
|
||||
return String.valueOf(punishment(player.getUUID(), PunishmentType.BAN) != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String muteReason(GenericPlayer player){
|
||||
Punishment mute = punishment(player.getUUID(), PunishmentType.MUTE);
|
||||
if(mute == null)
|
||||
return null;
|
||||
|
||||
return mute.getReason();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String banReason(GenericPlayer player){
|
||||
Punishment ban = punishment(player.getUUID(), PunishmentType.BAN);
|
||||
if(ban == null)
|
||||
return null;
|
||||
|
||||
return ban.getReason();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String muteDuration(GenericPlayer player){
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String banDuration(GenericPlayer player){
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String muteExpirationDate(GenericPlayer player, String pattern){
|
||||
Punishment mute = punishment(player.getUUID(), PunishmentType.MUTE);
|
||||
if(mute == null)
|
||||
return null;
|
||||
|
||||
if(mute.isPermanent())
|
||||
return "never";
|
||||
|
||||
return this.returnDate(pattern, mute.getEndDate().toEpochMilli());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String banExpirationDate(GenericPlayer player, String pattern){
|
||||
Punishment ban = punishment(player.getUUID(), PunishmentType.BAN);
|
||||
if(ban == null)
|
||||
return null;
|
||||
|
||||
if(ban.isPermanent())
|
||||
return "never";
|
||||
|
||||
return this.returnDate(pattern, ban.getEndDate().toEpochMilli());
|
||||
}
|
||||
|
||||
private Punishment punishment(UUID uuid, PunishmentType type){
|
||||
List<? extends KnownAccount> accounts = libertyBans.getAccountSupervisor()
|
||||
.findAccountsMatching(uuid)
|
||||
.join();
|
||||
|
||||
if(accounts.isEmpty())
|
||||
return null;
|
||||
|
||||
KnownAccount sorted = Collections.max(accounts, Comparator.comparing(KnownAccount::recorded));
|
||||
|
||||
return libertyBans.getSelector()
|
||||
.selectionByApplicabilityBuilder(sorted.uuid(), sorted.address())
|
||||
.type(type)
|
||||
.build()
|
||||
.getFirstSpecificPunishment()
|
||||
.toCompletableFuture()
|
||||
.join()
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.banplugins.providers;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import litebans.api.Database;
|
||||
import litebans.api.Entry;
|
||||
|
||||
public class LiteBansProvider implements PunishmentProvider{
|
||||
|
||||
@Override
|
||||
public String muted(GenericPlayer player){
|
||||
return String.valueOf(Database.get().isPlayerMuted(player.getUUID(), null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String banned(GenericPlayer player){
|
||||
return String.valueOf(Database.get().isPlayerBanned(player.getUUID(), null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String muteReason(GenericPlayer player){
|
||||
Entry mute = Database.get().getMute(player.getUUID(), null, null);
|
||||
if(mute == null)
|
||||
return null;
|
||||
|
||||
return mute.getReason();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String banReason(GenericPlayer player){
|
||||
Entry ban = Database.get().getBan(player.getUUID(), null, null);
|
||||
if(ban == null)
|
||||
return null;
|
||||
|
||||
return ban.getReason();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String muteDuration(GenericPlayer player){
|
||||
Entry mute = Database.get().getMute(player.getUUID(), null, null);
|
||||
if(mute == null)
|
||||
return null;
|
||||
|
||||
return mute.getDurationString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String banDuration(GenericPlayer player){
|
||||
Entry ban = Database.get().getBan(player.getUUID(), null, null);
|
||||
if(ban == null)
|
||||
return null;
|
||||
|
||||
return ban.getDurationString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String muteExpirationDate(GenericPlayer player, String pattern){
|
||||
Entry mute = Database.get().getMute(player.getUUID(), null, null);
|
||||
if(mute == null)
|
||||
return null;
|
||||
|
||||
if(mute.isPermanent())
|
||||
return "never";
|
||||
|
||||
return this.returnDate(pattern, mute.getDateEnd());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String banExpirationDate(GenericPlayer player, String pattern){
|
||||
Entry ban = Database.get().getBan(player.getUUID(), null, null);
|
||||
if(ban == null)
|
||||
return null;
|
||||
|
||||
if(ban.isPermanent())
|
||||
return "never";
|
||||
|
||||
return this.returnDate(pattern, ban.getDateEnd());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.banplugins.providers;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
public interface PunishmentProvider{
|
||||
|
||||
String muted(GenericPlayer player);
|
||||
|
||||
String banned(GenericPlayer player);
|
||||
|
||||
String muteReason(GenericPlayer player);
|
||||
|
||||
String banReason(GenericPlayer player);
|
||||
|
||||
String muteDuration(GenericPlayer player);
|
||||
|
||||
String banDuration(GenericPlayer player);
|
||||
|
||||
default String muteExpirationDate(GenericPlayer player){
|
||||
return muteExpirationDate(player, "dd, MMM yyyy HH:mm:ss");
|
||||
}
|
||||
|
||||
String muteExpirationDate(GenericPlayer player, String pattern);
|
||||
|
||||
default String banExpirationDate(GenericPlayer player){
|
||||
return banExpirationDate(player, "dd, MMM yyyy HH:mm:ss");
|
||||
}
|
||||
|
||||
String banExpirationDate(GenericPlayer player, String pattern);
|
||||
|
||||
default String returnDate(String pattern, long end){
|
||||
Date date = new Date(end);
|
||||
try{
|
||||
return new SimpleDateFormat(pattern).format(date);
|
||||
}catch(IllegalArgumentException ex){
|
||||
return new SimpleDateFormat("dd, MMM yyyy HH:mm:ss").format(date);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.banplugins.velocity;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.AdvancedServerListAPI;
|
||||
import ch.andre601.advancedserverlist.banplugins.BanPluginsList;
|
||||
import com.google.inject.Inject;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class VelocityBanPluginsAddon{
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger("ASLBanPluginsAddon");
|
||||
|
||||
private final ProxyServer proxy;
|
||||
|
||||
@Inject
|
||||
public VelocityBanPluginsAddon(ProxyServer proxy){
|
||||
this.proxy = proxy;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void init(ProxyInitializeEvent event){
|
||||
if(!proxy.getPluginManager().isLoaded("advancedserverlist")){
|
||||
logger.warn("AdvancedServerList is not enabled. This plugin requires it to function!");
|
||||
return;
|
||||
}
|
||||
|
||||
int loadedPlaceholders = loadPlaceholderProviders();
|
||||
if(loadedPlaceholders == 0){
|
||||
logger.warn("No compatible Ban plugin was found to register placeholders for.");
|
||||
}else{
|
||||
logger.info("Loaded {} Placeholder Set(s) for AdvancedServerList!", loadedPlaceholders);
|
||||
}
|
||||
}
|
||||
|
||||
private int loadPlaceholderProviders(){
|
||||
int loaded = 0;
|
||||
AdvancedServerListAPI api = AdvancedServerListAPI.get();
|
||||
|
||||
for(BanPluginsList entry : BanPluginsList.values()){
|
||||
if(!entry.supportsVelocity())
|
||||
continue;
|
||||
|
||||
if(proxy.getPluginManager().isLoaded(entry.getName().toLowerCase(Locale.ROOT))){
|
||||
logger.info("Registering Placeholders for " + entry.getName() + "...");
|
||||
api.addPlaceholderProvider(entry.getPlaceholderProvider());
|
||||
logger.info("Placeholders successfully registered!");
|
||||
|
||||
loaded++;
|
||||
}
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
name: 'ASLBanPluginsAddon'
|
||||
author: 'Andre_601'
|
||||
version: '${plugin.version}'
|
||||
description: 'AdvancedBan Add-on for AdvancedServerList to add placeholders to use.'
|
||||
|
||||
softDepends:
|
||||
- AdvancedServerList
|
||||
- AdvancedBan
|
||||
- LibertyBans
|
||||
- LiteBans
|
||||
|
||||
main: 'ch.andre601.advancedserverlist.banplugins.bungeecord.BungeeCordBanPluginsAddon'
|
||||
@@ -0,0 +1,22 @@
|
||||
name: 'ASLBanPluginsAddon'
|
||||
author: 'Andre_601'
|
||||
version: '${plugin.version}'
|
||||
|
||||
main: 'ch.andre601.advancedserverlist.banplugins.paper.PaperBanPluginsAddon'
|
||||
|
||||
api-version: '1.19'
|
||||
|
||||
dependencies:
|
||||
server:
|
||||
AdvancedServerList:
|
||||
required: false
|
||||
load: BEFORE
|
||||
AdvancedBan:
|
||||
required: false
|
||||
load: BEFORE
|
||||
LibertyBans:
|
||||
required: false
|
||||
load: BEFORE
|
||||
LiteBans:
|
||||
required: false
|
||||
load: BEFORE
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"id": "aslbanpluginsaddon",
|
||||
"name": "ASLBanPluginsAddon",
|
||||
"version": "${plugin.version}",
|
||||
"description": "${plugin.description}",
|
||||
"authors": [
|
||||
"Andre_601"
|
||||
],
|
||||
"dependencies": [
|
||||
{
|
||||
"id": "advancedserverlist",
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"id": "libertybans",
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"id": "litebans",
|
||||
"optional": true
|
||||
}
|
||||
],
|
||||
"main": "ch.andre601.advancedserverlist.banplugins.velocity.VelocityBanPluginsAddon"
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.bukkit.commands;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
|
||||
import ch.andre601.advancedserverlist.paper.commands.PaperCmdSender;
|
||||
import ch.andre601.advancedserverlist.spigot.commands.SpigotCmdSender;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public class CmdAdvancedServerList extends Command implements CommandExecutor{
|
||||
|
||||
private final PluginCore<?> plugin;
|
||||
private final BukkitAudiences audiences;
|
||||
|
||||
public CmdAdvancedServerList(PluginCore<?> plugin){
|
||||
this(plugin, null);
|
||||
}
|
||||
|
||||
public CmdAdvancedServerList(PluginCore<?> plugin, BukkitAudiences audiences){
|
||||
super(
|
||||
"advancedserverlist",
|
||||
"Main command of the plugin",
|
||||
"/asl [reload|help|clearCache]",
|
||||
Collections.singletonList("asl")
|
||||
);
|
||||
|
||||
this.plugin = plugin;
|
||||
this.audiences = audiences;
|
||||
}
|
||||
|
||||
public boolean execute(@NotNull CommandSender sender, @NotNull String s, @NotNull String[] args){
|
||||
plugin.getCore().getCommandHandler().handle(getSender(sender), args);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, @NotNull String[] args){
|
||||
plugin.getCore().getCommandHandler().handle(getSender(sender), args);
|
||||
return true;
|
||||
}
|
||||
|
||||
private CmdSender getSender(CommandSender sender){
|
||||
return audiences == null ? new PaperCmdSender(sender) : new SpigotCmdSender(sender, audiences);
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.bukkit.listeners;
|
||||
|
||||
import ch.andre601.advancedserverlist.bukkit.BukkitCore;
|
||||
import ch.andre601.advancedserverlist.bukkit.objects.placeholders.PAPIPlaceholders;
|
||||
import ch.andre601.advancedserverlist.spigot.SpigotCore;
|
||||
import ch.andre601.advancedserverlist.spigot.listeners.ProtocolLibEvents;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.server.ServerLoadEvent;
|
||||
|
||||
public class LoadEvent implements Listener{
|
||||
|
||||
private final BukkitCore<?> plugin;
|
||||
|
||||
private LoadEvent(BukkitCore<?> plugin){
|
||||
this.plugin = plugin;
|
||||
plugin.getServer().getPluginManager().registerEvents(this, plugin);
|
||||
}
|
||||
|
||||
public static void init(BukkitCore<?> plugin){
|
||||
new LoadEvent(plugin);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onServerLoad(ServerLoadEvent event){
|
||||
JoinEvent.init(plugin);
|
||||
WorldEvents.init(plugin);
|
||||
|
||||
// Populate WorldCache with all loaded worlds
|
||||
Bukkit.getWorlds().forEach(world -> plugin.getWorldCache().addWorld(world.getName(), world));
|
||||
|
||||
if(!(plugin instanceof SpigotCore spigotCore))
|
||||
return;
|
||||
|
||||
if(!Bukkit.getPluginManager().isPluginEnabled("ProtocolLib")){
|
||||
plugin.getCore().getPlugin().getPluginLogger().warn("ProtocolLib not found! AdvancedServerList requires it to work on Spigot!");
|
||||
|
||||
Bukkit.getPluginManager().disablePlugin(plugin);
|
||||
return;
|
||||
}
|
||||
|
||||
ProtocolLibEvents.init(spigotCore, ProtocolLibrary.getProtocolManager());
|
||||
|
||||
if(Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI"))
|
||||
spigotCore.setPapiPlaceholders(PAPIPlaceholders.init(spigotCore));
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.paper;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
|
||||
import io.papermc.paper.plugin.loader.PluginClasspathBuilder;
|
||||
import io.papermc.paper.plugin.loader.PluginLoader;
|
||||
import io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
|
||||
import org.eclipse.aether.artifact.DefaultArtifact;
|
||||
import org.eclipse.aether.graph.Dependency;
|
||||
import org.eclipse.aether.repository.RemoteRepository;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ASLLoader implements PluginLoader{
|
||||
@Override
|
||||
public void classloader(@NotNull PluginClasspathBuilder pluginClasspathBuilder){
|
||||
ComponentLogger logger = pluginClasspathBuilder.getContext().getLogger();
|
||||
|
||||
logger.info(Component.text("Loading Libraries for plugin..."));
|
||||
|
||||
MavenLibraryResolver resolver = new MavenLibraryResolver();
|
||||
|
||||
resolver.addRepository(new RemoteRepository.Builder(
|
||||
"paper-maven",
|
||||
"default",
|
||||
"https://repo.papermc.io/repository/maven-public/"
|
||||
).build());
|
||||
|
||||
logger.info(Component.text("Loading com.squareup.okhttp3:okhttp:4.10.0 from repo.papermc.io..."));
|
||||
|
||||
resolver.addDependency(new Dependency(new DefaultArtifact("com.squareup.okhttp3:okhttp:4.10.0"), null));
|
||||
pluginClasspathBuilder.addLibrary(resolver);
|
||||
|
||||
logger.info(Component.text("Loading libraries complete!"));
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.paper;
|
||||
|
||||
import ch.andre601.advancedserverlist.bukkit.BukkitCore;
|
||||
import ch.andre601.advancedserverlist.bukkit.commands.CmdAdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.bukkit.listeners.JoinEvent;
|
||||
import ch.andre601.advancedserverlist.bukkit.listeners.LoadEvent;
|
||||
import ch.andre601.advancedserverlist.bukkit.listeners.WorldEvents;
|
||||
import ch.andre601.advancedserverlist.bukkit.logging.BukkitLogger;
|
||||
import ch.andre601.advancedserverlist.bukkit.objects.placeholders.BukkitPlayerPlaceholders;
|
||||
import ch.andre601.advancedserverlist.bukkit.objects.placeholders.BukkitServerPlaceholders;
|
||||
import ch.andre601.advancedserverlist.bukkit.objects.placeholders.PAPIPlaceholders;
|
||||
import ch.andre601.advancedserverlist.bukkit.objects.WorldCache;
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler;
|
||||
import ch.andre601.advancedserverlist.paper.listeners.PaperPingEvent;
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bstats.charts.SimplePie;
|
||||
import org.bukkit.util.CachedServerIcon;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class PaperCore extends BukkitCore<CachedServerIcon>{
|
||||
|
||||
private final PluginLogger logger = new BukkitLogger(getLogger());
|
||||
|
||||
private AdvancedServerList<CachedServerIcon> core;
|
||||
private FaviconHandler<CachedServerIcon> faviconHandler = null;
|
||||
private PAPIPlaceholders<CachedServerIcon> papiPlaceholders = null;
|
||||
private WorldCache worldCache = null;
|
||||
|
||||
@Override
|
||||
public void onEnable(){
|
||||
this.core = AdvancedServerList.init(this, BukkitPlayerPlaceholders.init(), BukkitServerPlaceholders.init());
|
||||
|
||||
if(getServer().getPluginManager().isPluginEnabled("PlaceholderAPI"))
|
||||
papiPlaceholders = PAPIPlaceholders.init(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable(){
|
||||
if(papiPlaceholders != null){
|
||||
papiPlaceholders.unregister();
|
||||
papiPlaceholders = null;
|
||||
}
|
||||
|
||||
if(worldCache != null)
|
||||
worldCache = null;
|
||||
|
||||
getCore().disable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadCommands(){
|
||||
if(getServer().getCommandMap().register("asl", new CmdAdvancedServerList(this))){
|
||||
getPluginLogger().info("Registered /advancedserverlist:advancedserverlist");
|
||||
}else{
|
||||
getPluginLogger().info("Registered /asl:advancedserverlist");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadEvents(){
|
||||
LoadEvent.init(this);
|
||||
PaperPingEvent.init(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMetrics(){
|
||||
new Metrics(this, 15584).addCustomChart(new SimplePie("profiles",
|
||||
() -> String.valueOf(core.getFileHandler().getProfiles().size())
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearFaviconCache(){
|
||||
if(faviconHandler == null)
|
||||
return;
|
||||
|
||||
faviconHandler.clearCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdvancedServerList<CachedServerIcon> getCore(){
|
||||
return core;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getFolderPath(){
|
||||
return getDataFolder().toPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginLogger getPluginLogger(){
|
||||
return logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaviconHandler<CachedServerIcon> getFaviconHandler(){
|
||||
if(faviconHandler == null)
|
||||
faviconHandler = new FaviconHandler<>(core);
|
||||
|
||||
return faviconHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlatformName(){
|
||||
return getServer().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlatformVersion(){
|
||||
return getServer().getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLoader(){
|
||||
return "paper";
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldCache getWorldCache(){
|
||||
if(worldCache != null)
|
||||
return worldCache;
|
||||
|
||||
return (worldCache = new WorldCache());
|
||||
}
|
||||
}
|
||||
@@ -1,178 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.spigot;
|
||||
|
||||
import ch.andre601.advancedserverlist.bukkit.BukkitCore;
|
||||
import ch.andre601.advancedserverlist.bukkit.commands.CmdAdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.bukkit.logging.BukkitLogger;
|
||||
import ch.andre601.advancedserverlist.bukkit.objects.placeholders.BukkitPlayerPlaceholders;
|
||||
import ch.andre601.advancedserverlist.bukkit.objects.placeholders.BukkitServerPlaceholders;
|
||||
import ch.andre601.advancedserverlist.bukkit.objects.placeholders.PAPIPlaceholders;
|
||||
import ch.andre601.advancedserverlist.bukkit.objects.WorldCache;
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler;
|
||||
import ch.andre601.advancedserverlist.bukkit.listeners.LoadEvent;
|
||||
import com.comphenix.protocol.wrappers.WrappedServerPing;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bstats.charts.SimplePie;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class SpigotCore extends BukkitCore<WrappedServerPing.CompressedImage>{
|
||||
|
||||
private final PluginLogger logger = new BukkitLogger(getLogger());
|
||||
|
||||
private AdvancedServerList<WrappedServerPing.CompressedImage> core;
|
||||
private FaviconHandler<WrappedServerPing.CompressedImage> faviconHandler = null;
|
||||
private PAPIPlaceholders<WrappedServerPing.CompressedImage> papiPlaceholders = null;
|
||||
private WorldCache worldCache = null;
|
||||
|
||||
@Override
|
||||
public void onEnable(){
|
||||
try{
|
||||
Class.forName("io.papermc.paper.configuration.ConfigurationLoaders");
|
||||
|
||||
printPaperInfo();
|
||||
}catch(ClassNotFoundException ex){
|
||||
try{
|
||||
// Above class only exists since 1.19+, so this is a fallback for older versions.
|
||||
Class.forName("com.destroystokyo.paper.PaperConfig");
|
||||
|
||||
printPaperInfo();
|
||||
}catch(ClassNotFoundException ignored){}
|
||||
}
|
||||
|
||||
this.core = AdvancedServerList.init(this, BukkitPlayerPlaceholders.init(), BukkitServerPlaceholders.init());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable(){
|
||||
if(papiPlaceholders != null){
|
||||
papiPlaceholders.unregister();
|
||||
papiPlaceholders = null;
|
||||
}
|
||||
|
||||
if(worldCache != null)
|
||||
worldCache = null;
|
||||
|
||||
getCore().disable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadCommands(){
|
||||
PluginCommand cmd = getServer().getPluginCommand("advancedserverlist");
|
||||
if(cmd == null){
|
||||
getPluginLogger().warn("Unable to register command /advancedserverlist");
|
||||
return;
|
||||
}
|
||||
cmd.setExecutor(new CmdAdvancedServerList(this, BukkitAudiences.create(this)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadEvents(){
|
||||
LoadEvent.init(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMetrics(){
|
||||
new Metrics(this, 15584).addCustomChart(new SimplePie("profiles",
|
||||
() -> String.valueOf(core.getFileHandler().getProfiles().size())
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearFaviconCache(){
|
||||
if(faviconHandler == null)
|
||||
return;
|
||||
|
||||
faviconHandler.clearCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdvancedServerList<WrappedServerPing.CompressedImage> getCore(){
|
||||
return core;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getFolderPath(){
|
||||
return getDataFolder().toPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginLogger getPluginLogger(){
|
||||
return logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaviconHandler<WrappedServerPing.CompressedImage> getFaviconHandler(){
|
||||
if(faviconHandler == null)
|
||||
faviconHandler = new FaviconHandler<>(core);
|
||||
|
||||
return faviconHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlatformName(){
|
||||
return getServer().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlatformVersion(){
|
||||
return getServer().getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLoader(){
|
||||
return "spigot";
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldCache getWorldCache(){
|
||||
if(worldCache != null)
|
||||
return worldCache;
|
||||
|
||||
return (worldCache = new WorldCache());
|
||||
}
|
||||
|
||||
public void setPapiPlaceholders(PAPIPlaceholders<WrappedServerPing.CompressedImage> papiPlaceholders){
|
||||
this.papiPlaceholders = papiPlaceholders;
|
||||
}
|
||||
|
||||
private void printPaperInfo(){
|
||||
getPluginLogger().warn("======================================================================================");
|
||||
getPluginLogger().warn("You are using the Spigot version of AdvancedServerList on a Paper server.");
|
||||
getPluginLogger().warn("It is recommended to use the dedicated Paper version, to benefit from the");
|
||||
getPluginLogger().warn("following improvements:");
|
||||
getPluginLogger().warn(" - No downloading of external Libraries already provided by Paper (i.e. Adventure).");
|
||||
getPluginLogger().warn(" - No dependency on ProtocolLib thanks to provided Events.");
|
||||
getPluginLogger().warn("");
|
||||
getPluginLogger().warn("AdvancedServerList may work as normal, but consider using the PaperMC version instead!");
|
||||
getPluginLogger().warn("======================================================================================");
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.spigot.commands;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
public class SpigotCmdSender implements CmdSender{
|
||||
|
||||
private final CommandSender sender;
|
||||
private final BukkitAudiences bukkitAudiences;
|
||||
|
||||
public SpigotCmdSender(CommandSender sender, BukkitAudiences bukkitAudiences){
|
||||
this.sender = sender;
|
||||
this.bukkitAudiences = bukkitAudiences;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission){
|
||||
return sender.hasPermission(permission) || sender.hasPermission("advancedserverlist.admin");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMsg(String msg, Object... args){
|
||||
bukkitAudiences.sender(sender).sendMessage(ComponentParser.text(String.format(msg, args)).toComponent());
|
||||
}
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.spigot.listeners;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.events.GenericServerListEvent;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
|
||||
import ch.andre601.advancedserverlist.bukkit.BukkitCore;
|
||||
import ch.andre601.advancedserverlist.bukkit.listeners.PreServerListSetEventImpl;
|
||||
import ch.andre601.advancedserverlist.bukkit.objects.impl.BukkitServerImpl;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.events.GenericEventWrapper;
|
||||
import ch.andre601.advancedserverlist.core.objects.CachedPlayer;
|
||||
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
|
||||
import ch.andre601.advancedserverlist.core.profiles.replacer.StringReplacer;
|
||||
import ch.andre601.advancedserverlist.spigot.SpigotCore;
|
||||
import ch.andre601.advancedserverlist.bukkit.objects.impl.BukkitPlayerImpl;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
import com.comphenix.protocol.wrappers.WrappedServerPing;
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.*;
|
||||
|
||||
public class ProtocolLibEventWrapper implements GenericEventWrapper<WrappedServerPing.CompressedImage, BukkitPlayerImpl>{
|
||||
|
||||
private final SpigotCore plugin;
|
||||
private final PacketEvent event;
|
||||
private final WrappedServerPing ping;
|
||||
|
||||
public ProtocolLibEventWrapper(SpigotCore plugin, PacketEvent event){
|
||||
this.plugin = plugin;
|
||||
this.event = event;
|
||||
this.ping = event.getPacket().getServerPings().read(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericServerListEvent callEvent(ProfileEntry entry){
|
||||
PreServerListSetEventImpl event = new PreServerListSetEventImpl(entry);
|
||||
plugin.getServer().getPluginManager().callEvent(event);
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxPlayers(int maxPlayers){
|
||||
ping.setPlayersMaximum(maxPlayers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMotd(Component component){
|
||||
ping.setMotD(WrappedChatComponent.fromJson(GsonComponentSerializer.gson().serialize(component)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hidePlayers(){
|
||||
ping.setPlayersVisible(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerCount(String name){
|
||||
ping.setVersionName(name);
|
||||
ping.setVersionProtocol(-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayers(List<String> lines, BukkitPlayerImpl player, GenericServer server){
|
||||
List<WrappedGameProfile> players = new ArrayList<>(lines.size());
|
||||
|
||||
for(String line : lines){
|
||||
String parsed = ComponentParser.text(line)
|
||||
.modifyText(text -> StringReplacer.replace(text, player, server))
|
||||
.modifyText(text -> {
|
||||
if(plugin.getServer().getPluginManager().isPluginEnabled("PlaceholderAPI"))
|
||||
return PlaceholderAPI.setPlaceholders(player.getPlayer(), text);
|
||||
|
||||
return text;
|
||||
})
|
||||
.toString();
|
||||
|
||||
players.add(new WrappedGameProfile(UUID.randomUUID(), parsed));
|
||||
}
|
||||
|
||||
ping.setPlayers(players);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFavicon(WrappedServerPing.CompressedImage favicon){
|
||||
ping.setFavicon(favicon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultFavicon(){
|
||||
ping.setFavicon(ping.getFavicon());
|
||||
}
|
||||
|
||||
// Not used in ProtocolLib
|
||||
@Override
|
||||
public void updateEvent(){
|
||||
event.getPacket().getServerPings().write(0, ping);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInvalidProtocol(){
|
||||
return event.getPlayer().getAddress() == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion(){
|
||||
return ping.getVersionProtocol();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOnlinePlayers(){
|
||||
return ping.getPlayersOnline();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxPlayers(){
|
||||
return ping.getPlayersMaximum();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlayerIP(){
|
||||
return event.getPlayer().getAddress() == null ? "UNKNOWN" : event.getPlayer().getAddress().getHostString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parsePAPIPlaceholders(String text, BukkitPlayerImpl player){
|
||||
if(plugin.getServer().getPluginManager().isPluginEnabled("PlaceholderAPI"))
|
||||
return PlaceholderAPI.setPlaceholders(player.getPlayer(), text);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVirtualHost(){
|
||||
String host = this.resolveHost(event.getPlayer().getAddress());
|
||||
if(host == null)
|
||||
return null;
|
||||
|
||||
return ProtocolLibEvents.getHostAddresses().get(host);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BukkitCore<WrappedServerPing.CompressedImage> getPlugin(){
|
||||
return plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BukkitPlayerImpl createPlayer(CachedPlayer player, int protocol){
|
||||
OfflinePlayer pl = Bukkit.getOfflinePlayer(player.getUuid());
|
||||
|
||||
return new BukkitPlayerImpl(pl.hasPlayedBefore() ? pl : null, player, protocol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericServer createGenericServer(int playersOnline, int playersMax, String host){
|
||||
return new BukkitServerImpl(plugin.getWorldCache().worlds(), playersOnline, playersMax, host);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrappedServerPing.CompressedImage createFavicon(BufferedImage image) throws Exception{
|
||||
return WrappedServerPing.CompressedImage.fromPng(image);
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.spigot.listeners;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.events.PingEventHandler;
|
||||
import ch.andre601.advancedserverlist.spigot.SpigotCore;
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import com.comphenix.protocol.events.ListenerPriority;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class ProtocolLibEvents implements Listener{
|
||||
|
||||
private final ProtocolManager protocolManager;
|
||||
|
||||
private static final Map<String, String> hostAddresses = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
private ProtocolLibEvents(SpigotCore plugin, ProtocolManager protocolManager){
|
||||
this.protocolManager = protocolManager;
|
||||
|
||||
loadPacketListener(plugin);
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin);
|
||||
}
|
||||
|
||||
public static void init(SpigotCore plugin, ProtocolManager protocolManager){
|
||||
new ProtocolLibEvents(plugin, protocolManager);
|
||||
}
|
||||
|
||||
public static Map<String, String> getHostAddresses(){
|
||||
return hostAddresses;
|
||||
}
|
||||
|
||||
private void loadPacketListener(SpigotCore spigotPlugin){
|
||||
protocolManager.addPacketListener(new PacketAdapter(spigotPlugin, ListenerPriority.LOW, PacketType.Handshake.Client.SET_PROTOCOL){
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent event){
|
||||
InetSocketAddress address = event.getPlayer().getAddress();
|
||||
if(address == null)
|
||||
return;
|
||||
|
||||
String host = event.getPacket().getStrings().read(0);
|
||||
|
||||
hostAddresses.put(address.getHostString(), host);
|
||||
}
|
||||
});
|
||||
|
||||
protocolManager.addPacketListener(new PacketAdapter(spigotPlugin, ListenerPriority.LOW, PacketType.Status.Server.SERVER_INFO){
|
||||
@Override
|
||||
public void onPacketSending(PacketEvent event){
|
||||
PingEventHandler.handleEvent(new ProtocolLibEventWrapper(spigotPlugin, event));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -64,60 +64,78 @@
|
||||
<version>${api.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- BungeeCord API -->
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.19-R0.1-SNAPSHOT</version>
|
||||
<version>1.20-R0.2</version>
|
||||
<type>jar</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.19-R0.1-SNAPSHOT</version>
|
||||
<version>1.20-R0.2</version>
|
||||
<type>javadoc</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-api</artifactId>
|
||||
<version>4.13.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Adventure Library -->
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-api</artifactId>
|
||||
<version>${dependency.adventure-api}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-text-minimessage</artifactId>
|
||||
<version>4.13.0</version>
|
||||
<scope>compile</scope>
|
||||
<version>${dependency.adventure-text-minimessage}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-platform-bungeecord</artifactId>
|
||||
<version>4.1.2</version>
|
||||
<scope>compile</scope>
|
||||
<version>${dependency.adventure-platform-bungeecord}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Metrics -->
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bungeecord</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<version>3.1.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Configurate -->
|
||||
<dependency>
|
||||
<groupId>org.spongepowered</groupId>
|
||||
<artifactId>configurate-yaml</artifactId>
|
||||
<version>4.1.2</version>
|
||||
<scope>compile</scope>
|
||||
<version>${dependency.configurate-yaml}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Plugins -->
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>4.10.0</version>
|
||||
<groupId>com.github.LeonMangler</groupId>
|
||||
<artifactId>PremiumVanishAPI</artifactId>
|
||||
<version>2.9.0-4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Other -->
|
||||
<dependency>
|
||||
<groupId>com.alessiodp.libby</groupId>
|
||||
<artifactId>libby-bungee</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>AdvancedServerList-BungeeCord-${plugin.version}${project.build.commit}</finalName>
|
||||
<finalName>AdvancedServerList-BungeeCord-${plugin.version}</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<filtering>true</filtering>
|
||||
@@ -128,30 +146,22 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.11.0</version>
|
||||
<version>3.13.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<version>3.6.0</version>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.spongepowered.configurate</pattern>
|
||||
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.configurate</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>net.kyori</pattern>
|
||||
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.kyori</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.bstats</pattern>
|
||||
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.bstats</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>okhttp3</pattern>
|
||||
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.okhttp3</shadedPattern>
|
||||
<pattern>net.byteflux.libby</pattern>
|
||||
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.libby</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<filters>
|
||||
@@ -159,6 +169,7 @@
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
|
||||
@@ -29,33 +29,74 @@ import ch.andre601.advancedserverlist.bungeecord.commands.CmdAdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.bungeecord.listeners.JoinEvent;
|
||||
import ch.andre601.advancedserverlist.bungeecord.listeners.PingEvent;
|
||||
import ch.andre601.advancedserverlist.bungeecord.logging.BungeeLogger;
|
||||
import ch.andre601.advancedserverlist.bungeecord.objects.BungeePlayerPlaceholders;
|
||||
import ch.andre601.advancedserverlist.bungeecord.objects.BungeeServerPlaceholders;
|
||||
import ch.andre601.advancedserverlist.bungeecord.objects.placeholders.BungeePlayerPlaceholders;
|
||||
import ch.andre601.advancedserverlist.bungeecord.objects.placeholders.BungeeProxyPlaceholders;
|
||||
import ch.andre601.advancedserverlist.bungeecord.objects.placeholders.BungeeServerPlaceholders;
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
|
||||
import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler;
|
||||
import ch.andre601.advancedserverlist.core.profiles.handlers.FaviconHandler;
|
||||
import com.alessiodp.libby.BungeeLibraryManager;
|
||||
import com.alessiodp.libby.Library;
|
||||
import de.myzelyam.api.vanish.BungeeVanishAPI;
|
||||
import net.kyori.adventure.platform.bungeecord.BungeeAudiences;
|
||||
import net.md_5.bungee.api.Favicon;
|
||||
import net.md_5.bungee.api.ServerPing;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import org.bstats.bungeecord.Metrics;
|
||||
import org.bstats.charts.SimplePie;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BungeeCordCore extends Plugin implements PluginCore<Favicon>{
|
||||
|
||||
private final PluginLogger logger = new BungeeLogger(this);
|
||||
private final Map<String, ServerPing> fetchedServers = new ConcurrentHashMap<>();
|
||||
|
||||
private boolean successfulLoad = false;
|
||||
|
||||
private AdvancedServerList<Favicon> core;
|
||||
private FaviconHandler<Favicon> faviconHandler = null;
|
||||
private final PluginLogger logger = new BungeeLogger(getLogger());
|
||||
private BungeeAudiences audiences = null;
|
||||
|
||||
private BungeeLibraryManager libraryManager = null;
|
||||
|
||||
@Override
|
||||
public void onLoad(){
|
||||
logger.info("Loading libraries. This may take a while...");
|
||||
successfulLoad = loadLibs();
|
||||
if(successfulLoad){
|
||||
logger.info("Library loading completed successfully!");
|
||||
}else{
|
||||
logger.warn("Library loading completed with errors!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable(){
|
||||
this.core = AdvancedServerList.init(this, BungeePlayerPlaceholders.init(), BungeeServerPlaceholders.init());
|
||||
if(!successfulLoad){
|
||||
logger.warn("There were issues during the plugins Loading-phase! Please see the console for possible reasons.");
|
||||
logger.warn("The plugin won't be enabled to avoid problems.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.audiences = BungeeAudiences.create(this);
|
||||
this.core = AdvancedServerList.init(this,
|
||||
BungeePlayerPlaceholders.init(), BungeeServerPlaceholders.init(this), BungeeProxyPlaceholders.init(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable(){
|
||||
core.disable();
|
||||
getProxy().getScheduler().cancel(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -77,12 +118,39 @@ public class BungeeCordCore extends Plugin implements PluginCore<Favicon>{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFaviconHandler(AdvancedServerList<Favicon> core){
|
||||
faviconHandler = new FaviconHandler<>(core);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearFaviconCache(){
|
||||
if(faviconHandler == null)
|
||||
return;
|
||||
|
||||
faviconHandler.clearCache();
|
||||
faviconHandler.cleanCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void downloadLibrary(String groupId, String artifactId, String version){
|
||||
if(libraryManager == null){
|
||||
libraryManager = new BungeeLibraryManager(this);
|
||||
libraryManager.addRepository("https://repo.papermc.io/repository/maven-public");
|
||||
}
|
||||
|
||||
Library lib = Library.builder()
|
||||
.groupId(groupId)
|
||||
.artifactId(artifactId)
|
||||
.version(version)
|
||||
.resolveTransitiveDependencies(true)
|
||||
.build();
|
||||
|
||||
libraryManager.loadLibrary(lib);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startScheduler(){
|
||||
getProxy().getScheduler().schedule(this, this::fetchServers, 0, 10, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,17 +177,67 @@ public class BungeeCordCore extends Plugin implements PluginCore<Favicon>{
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlatformName(){
|
||||
return getProxy().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlatformVersion(){
|
||||
return getProxy().getVersion();
|
||||
public String getPlatformInfo(){
|
||||
return getProxy().getName() + " " + getProxy().getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLoader(){
|
||||
return "bungeecord";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPluginEnabled(String plugin){
|
||||
return getProxy().getPluginManager().getPlugin(plugin) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Favicon createFavicon(BufferedImage image) throws IllegalArgumentException{
|
||||
return Favicon.create(image);
|
||||
}
|
||||
|
||||
public BungeeAudiences getAudiences(){
|
||||
return audiences;
|
||||
}
|
||||
|
||||
public int getOnlinePlayers(ServerInfo server){
|
||||
List<ProxiedPlayer> players = new ArrayList<>(server == null ? getProxy().getPlayers() : server.getPlayers());
|
||||
|
||||
// Exclude players when PremiumVanish is enabled and player is hidden.
|
||||
if(getProxy().getPluginManager().getPlugin("PremiumVanish") != null){
|
||||
players.removeIf(BungeeVanishAPI::isInvisible);
|
||||
}
|
||||
|
||||
return players.size();
|
||||
}
|
||||
|
||||
public Map<String, ServerPing> getFetchedServers(){
|
||||
return fetchedServers;
|
||||
}
|
||||
|
||||
private boolean loadLibs(){
|
||||
try{
|
||||
if(libraryManager == null){
|
||||
libraryManager = new BungeeLibraryManager(this);
|
||||
libraryManager.addRepository("https://repo.papermc.io/repository/maven-public");
|
||||
}
|
||||
|
||||
libraryManager.configureFromJSON("dependencies.json");
|
||||
|
||||
return true;
|
||||
}catch(Exception ex){
|
||||
logger.warn("Encountered an Exception while trying to load dependencies.", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchServers(){
|
||||
fetchedServers.clear();
|
||||
getProxy().getServers().forEach((name, server) -> server.ping((ping, throwable) -> {
|
||||
if(throwable != null)
|
||||
return;
|
||||
|
||||
fetchedServers.put(name, ping);
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -33,16 +33,14 @@ import net.md_5.bungee.api.plugin.Command;
|
||||
public class CmdAdvancedServerList extends Command{
|
||||
|
||||
private final BungeeCordCore plugin;
|
||||
private final BungeeAudiences audiences;
|
||||
|
||||
public CmdAdvancedServerList(BungeeCordCore plugin){
|
||||
super("advancedserverlist", "advancedserverlist.command.asl", "asl");
|
||||
this.plugin = plugin;
|
||||
this.audiences = BungeeAudiences.create(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args){
|
||||
plugin.getCore().getCommandHandler().handle(new BungeeCmdSender(sender, audiences), args);
|
||||
plugin.getCore().getCommandHandler().handle(new BungeeCmdSender(sender, plugin.getAudiences()), args);
|
||||
}
|
||||
}
|
||||
@@ -25,12 +25,14 @@
|
||||
|
||||
package ch.andre601.advancedserverlist.bungeecord.listeners;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.events.GenericServerListEvent;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
|
||||
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
|
||||
import ch.andre601.advancedserverlist.bungeecord.objects.BungeePlayerImpl;
|
||||
import ch.andre601.advancedserverlist.api.events.GenericServerListEvent;
|
||||
import ch.andre601.advancedserverlist.bungeecord.objects.BungeeProxyImpl;
|
||||
import ch.andre601.advancedserverlist.bungeecord.objects.impl.BungeePlayerImpl;
|
||||
import ch.andre601.advancedserverlist.bungeecord.objects.impl.BungeeProxyImpl;
|
||||
import ch.andre601.advancedserverlist.core.compat.maintenance.MaintenanceUtil;
|
||||
import ch.andre601.advancedserverlist.core.compat.papi.PAPIUtil;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.events.GenericEventWrapper;
|
||||
import ch.andre601.advancedserverlist.core.objects.CachedPlayer;
|
||||
@@ -42,9 +44,9 @@ import net.md_5.bungee.api.Favicon;
|
||||
import net.md_5.bungee.api.ServerPing;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.ProxyPingEvent;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -77,6 +79,11 @@ public class BungeeEventWrapper implements GenericEventWrapper<Favicon, BungeePl
|
||||
ping.getPlayers().setMax(maxPlayers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnlinePlayers(int onlinePlayers){
|
||||
ping.getPlayers().setOnline(onlinePlayers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMotd(Component component){
|
||||
ping.setDescriptionComponent(new TextComponent(BungeeComponentSerializer.get().serialize(component)));
|
||||
@@ -100,8 +107,9 @@ public class BungeeEventWrapper implements GenericEventWrapper<Favicon, BungeePl
|
||||
for(int i = 0; i < players.length; i++){
|
||||
String parsed = ComponentParser.text(lines.get(i))
|
||||
.modifyText(text -> StringReplacer.replace(text, player, server))
|
||||
.modifyText(text -> parsePAPIPlaceholders(text, player))
|
||||
.toString();
|
||||
|
||||
|
||||
ServerPing.PlayerInfo pi = new ServerPing.PlayerInfo(parsed, UUID.randomUUID());
|
||||
players[i] = pi;
|
||||
}
|
||||
@@ -131,14 +139,22 @@ public class BungeeEventWrapper implements GenericEventWrapper<Favicon, BungeePl
|
||||
return protocol == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMaintenanceModeActive(){
|
||||
if(plugin.getProxy().getPluginManager().getPlugin("Maintenance") == null)
|
||||
return false;
|
||||
|
||||
return MaintenanceUtil.get().isMaintenanceEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion(){
|
||||
return this.protocol.getProtocol();
|
||||
return event.getConnection().getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOnlinePlayers(){
|
||||
return ping.getPlayers().getOnline();
|
||||
return plugin.getOnlinePlayers(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -153,7 +169,25 @@ public class BungeeEventWrapper implements GenericEventWrapper<Favicon, BungeePl
|
||||
|
||||
@Override
|
||||
public String parsePAPIPlaceholders(String text, BungeePlayerImpl player){
|
||||
return text;
|
||||
if(plugin.getProxy().getPluginManager().getPlugin("PAPIProxyBridge") == null)
|
||||
return text;
|
||||
|
||||
if(!PAPIUtil.get().isCompatible())
|
||||
return text;
|
||||
|
||||
String server = PAPIUtil.get().getServer();
|
||||
if(server == null || server.isEmpty())
|
||||
return text;
|
||||
|
||||
ServerInfo serverInfo = plugin.getProxy().getServerInfo(server);
|
||||
if(serverInfo == null || serverInfo.getPlayers().isEmpty())
|
||||
return text;
|
||||
|
||||
ProxiedPlayer carrier = PAPIUtil.get().getPlayer(serverInfo.getPlayers());
|
||||
if(carrier == null)
|
||||
return text;
|
||||
|
||||
return PAPIUtil.get().parse(text, carrier.getUniqueId(), player.getUUID());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -177,9 +211,4 @@ public class BungeeEventWrapper implements GenericEventWrapper<Favicon, BungeePl
|
||||
|
||||
return new BungeeProxyImpl(servers, playersOnline, playersMax, host);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Favicon createFavicon(BufferedImage image) throws IllegalArgumentException{
|
||||
return Favicon.create(image);
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@
|
||||
package ch.andre601.advancedserverlist.bungeecord.listeners;
|
||||
|
||||
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
|
||||
import ch.andre601.advancedserverlist.bungeecord.commands.BungeeCmdSender;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.PostLoginEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
@@ -34,7 +35,6 @@ import net.md_5.bungee.event.EventHandler;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
public class JoinEvent implements Listener{
|
||||
|
||||
private final BungeeCordCore plugin;
|
||||
|
||||
public JoinEvent(BungeeCordCore plugin){
|
||||
@@ -48,5 +48,14 @@ public class JoinEvent implements Listener{
|
||||
ProxiedPlayer player = event.getPlayer();
|
||||
|
||||
plugin.getCore().getPlayerHandler().addPlayer(address.getHostString(), player.getName(), player.getUniqueId());
|
||||
|
||||
if(player.hasPermission("advancedserverlist.admin") || player.hasPermission("advancedserverlist.updatecheck")){
|
||||
if(plugin.getAudiences() == null || plugin.getCore().getUpdateChecker() == null)
|
||||
return;
|
||||
|
||||
BungeeCmdSender sender = new BungeeCmdSender(player, plugin.getAudiences());
|
||||
|
||||
plugin.getCore().getUpdateChecker().performCachedUpdateCheck(sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,12 +25,13 @@
|
||||
|
||||
package ch.andre601.advancedserverlist.bungeecord.listeners;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.bungeecord.events.PostServerListSetEvent;
|
||||
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
|
||||
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
|
||||
import ch.andre601.advancedserverlist.core.events.PingEventHandler;
|
||||
import net.md_5.bungee.api.event.ProxyPingEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
import net.md_5.bungee.event.EventPriority;
|
||||
|
||||
public class PingEvent implements Listener{
|
||||
|
||||
@@ -41,8 +42,10 @@ public class PingEvent implements Listener{
|
||||
plugin.getProxy().getPluginManager().registerListener(plugin, this);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
@EventHandler(priority = 90) // Maintenance has Event Priority 80, so we need to be AFTER it.
|
||||
public void onProxyPing(ProxyPingEvent event){
|
||||
PingEventHandler.handleEvent(new BungeeEventWrapper(plugin, event));
|
||||
ProfileEntry entry = PingEventHandler.handleEvent(new BungeeEventWrapper(plugin, event));
|
||||
|
||||
plugin.getProxy().getPluginManager().callEvent(new PostServerListSetEvent(entry));
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
package ch.andre601.advancedserverlist.bungeecord.logging;
|
||||
|
||||
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
|
||||
import java.util.logging.Level;
|
||||
@@ -32,10 +33,24 @@ import java.util.logging.Logger;
|
||||
|
||||
public class BungeeLogger implements PluginLogger{
|
||||
|
||||
private final BungeeCordCore plugin;
|
||||
private final Logger logger;
|
||||
|
||||
public BungeeLogger(Logger logger){
|
||||
this.logger = logger;
|
||||
public BungeeLogger(BungeeCordCore plugin){
|
||||
this.plugin = plugin;
|
||||
this.logger = plugin.getLogger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(Class<?> clazz, String msg, Object... args){
|
||||
if(plugin.isDebugEnabled())
|
||||
info("[DEBUG] [" + clazz.getSimpleName() + "] " + msg, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debugWarn(Class<?> clazz, String msg, Object... args){
|
||||
if(plugin.isDebugEnabled())
|
||||
warn("[DEBUG] [" + clazz.getSimpleName() + "] " + msg, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,17 +23,24 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.profiles.players;
|
||||
package ch.andre601.advancedserverlist.bungeecord.objects.impl;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.bungeecord.objects.BungeePlayer;
|
||||
import ch.andre601.advancedserverlist.core.objects.CachedPlayer;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class GenericPlayerImpl implements GenericPlayer{
|
||||
public class BungeePlayerImpl implements BungeePlayer{
|
||||
|
||||
protected String name = null;
|
||||
protected int protocol = 0;
|
||||
protected UUID uuid = null;
|
||||
private final String name;
|
||||
private final int protocol;
|
||||
private final UUID uuid;
|
||||
|
||||
public BungeePlayerImpl(CachedPlayer player, int protocol){
|
||||
this.name = player.name();
|
||||
this.protocol = protocol;
|
||||
this.uuid = player.uuid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName(){
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,7 +23,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.bungeecord.objects;
|
||||
package ch.andre601.advancedserverlist.bungeecord.objects.impl;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.bungeecord.objects.BungeeProxy;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,7 +23,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.bungeecord.objects;
|
||||
package ch.andre601.advancedserverlist.bungeecord.objects.placeholders;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
@@ -0,0 +1,61 @@
|
||||
package ch.andre601.advancedserverlist.bungeecord.objects.placeholders;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
|
||||
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||
import net.md_5.bungee.api.ServerPing;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
|
||||
public class BungeeProxyPlaceholders extends PlaceholderProvider{
|
||||
|
||||
private final BungeeCordCore plugin;
|
||||
|
||||
private BungeeProxyPlaceholders(BungeeCordCore plugin){
|
||||
super("proxy");
|
||||
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public static BungeeProxyPlaceholders init(BungeeCordCore plugin){
|
||||
return new BungeeProxyPlaceholders(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
|
||||
String[] args = placeholder.split("\\s", 2);
|
||||
if(args.length < 2)
|
||||
return null;
|
||||
|
||||
ServerPing ping = plugin.getFetchedServers().get(args[1]);
|
||||
|
||||
return switch(args[0]){
|
||||
case "status" -> ping == null ? "offline" : "online";
|
||||
case "motd" -> {
|
||||
if(ping == null)
|
||||
yield "none";
|
||||
|
||||
BaseComponent[] components = new BaseComponent[]{ping.getDescriptionComponent()};
|
||||
Component component = BungeeComponentSerializer.get().deserialize(components);
|
||||
|
||||
yield ComponentParser.toMMString(component);
|
||||
}
|
||||
case "players" -> {
|
||||
if(ping == null)
|
||||
yield "0";
|
||||
|
||||
yield String.valueOf(ping.getPlayers().getOnline());
|
||||
}
|
||||
case "maxPlayers" -> {
|
||||
if(ping == null)
|
||||
yield "0";
|
||||
|
||||
yield String.valueOf(ping.getPlayers().getMax());
|
||||
}
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,24 +23,28 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.bungeecord.objects;
|
||||
package ch.andre601.advancedserverlist.bungeecord.objects.placeholders;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
|
||||
import ch.andre601.advancedserverlist.api.bungeecord.objects.BungeeProxy;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
public class BungeeServerPlaceholders extends PlaceholderProvider{
|
||||
|
||||
private BungeeServerPlaceholders(){
|
||||
private final BungeeCordCore plugin;
|
||||
|
||||
private BungeeServerPlaceholders(BungeeCordCore plugin){
|
||||
super("server");
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public static BungeeServerPlaceholders init(){
|
||||
return new BungeeServerPlaceholders();
|
||||
public static BungeeServerPlaceholders init(BungeeCordCore plugin){
|
||||
return new BungeeServerPlaceholders(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -53,14 +57,18 @@ public class BungeeServerPlaceholders extends PlaceholderProvider{
|
||||
return switch(args[0]){
|
||||
case "playersOnline" -> {
|
||||
if(args.length >= 2){
|
||||
int players = 0;
|
||||
String[] servers = args[1].split(",");
|
||||
|
||||
for(int i = 1; i < args.length; i++){
|
||||
ServerInfo info = proxy.getServers().get(args[i]);
|
||||
int players = 0;
|
||||
for(String serverName : servers){
|
||||
if(serverName.isEmpty())
|
||||
continue;
|
||||
|
||||
ServerInfo info = proxy.getServers().get(serverName.strip());
|
||||
if(info == null)
|
||||
continue;
|
||||
|
||||
players += info.getPlayers().size();
|
||||
players += plugin.getOnlinePlayers(info);
|
||||
}
|
||||
|
||||
yield String.valueOf(players);
|
||||
@@ -71,16 +79,18 @@ public class BungeeServerPlaceholders extends PlaceholderProvider{
|
||||
case "playersMax" -> {
|
||||
// ServerInfo doesn't provide the max players that could join, so we won't allow an extra argument
|
||||
if(args.length >= 2)
|
||||
yield null;
|
||||
yield "";
|
||||
|
||||
yield String.valueOf(proxy.getPlayersMax());
|
||||
}
|
||||
case "host" -> {
|
||||
if(args.length > 2)
|
||||
yield null;
|
||||
yield "";
|
||||
|
||||
if(args.length == 2){
|
||||
if(args.length == 2 && args[1] != null && !args[1].isEmpty()){
|
||||
ServerInfo info = proxy.getServers().get(args[1]);
|
||||
if(info == null)
|
||||
yield "";
|
||||
|
||||
yield String.valueOf(((InetSocketAddress)info.getSocketAddress()).getHostString());
|
||||
}
|
||||
@@ -3,4 +3,9 @@ author: 'Andre_601'
|
||||
version: '${plugin.version}'
|
||||
description: '${plugin.description}'
|
||||
|
||||
softDepends:
|
||||
- PAPIProxyBridge
|
||||
- Maintenance
|
||||
- ServerListPlus
|
||||
|
||||
main: 'ch.andre601.advancedserverlist.bungeecord.BungeeCordCore'
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"libraries": [
|
||||
{
|
||||
"groupId": "net{}kyori",
|
||||
"artifactId": "adventure-api",
|
||||
"version": "${dependency.adventure-api}",
|
||||
"resolveTransitiveDependencies": true
|
||||
},
|
||||
{
|
||||
"groupId": "net{}kyori",
|
||||
"artifactId": "adventure-text-minimessage",
|
||||
"version": "${dependency.adventure-text-minimessage}",
|
||||
"resolveTransitiveDependencies": true
|
||||
},
|
||||
{
|
||||
"groupId": "net{}kyori",
|
||||
"artifactId": "adventure-platform-bungeecord",
|
||||
"version": "${dependency.adventure-platform-bungeecord}",
|
||||
"resolveTransitiveDependencies": true
|
||||
},
|
||||
{
|
||||
"groupId": "org{}spongepowered",
|
||||
"artifactId": "configurate-yaml",
|
||||
"version": "${dependency.configurate-yaml}",
|
||||
"resolveTransitiveDependencies": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -41,6 +41,7 @@
|
||||
<properties>
|
||||
<maven.compiler.source>16</maven.compiler.source>
|
||||
<maven.compiler.target>16</maven.compiler.target>
|
||||
<plugin.config.version>6</plugin.config.version>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
@@ -48,6 +49,22 @@
|
||||
<id>flexver-repo</id>
|
||||
<url>https://repo.sleeping.town/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>papiproxybridge-repo</id>
|
||||
<url>https://repo.william278.net/releases/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>sonatype</id>
|
||||
<url>https://s01.oss.sonatype.org/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>codeberg</id>
|
||||
<url>https://codeberg.org/api/packages/Andre601/maven/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>codemc</id>
|
||||
<url>https://repo.codemc.io/repository/maven-releases/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
@@ -57,66 +74,96 @@
|
||||
<version>${api.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.andre601</groupId>
|
||||
<artifactId>expressionparser</artifactId>
|
||||
<version>1.4.3</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spongepowered</groupId>
|
||||
<artifactId>configurate-yaml</artifactId>
|
||||
<version>4.1.2</version>
|
||||
<version>${dependency.configurate-yaml}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spongepowered</groupId>
|
||||
<artifactId>configurate-hocon</artifactId>
|
||||
<version>4.1.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- MiniMessage -->
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-api</artifactId>
|
||||
<version>4.14.0</version>
|
||||
<version>${dependency.adventure-api}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-text-minimessage</artifactId>
|
||||
<version>4.14.0</version>
|
||||
<version>${dependency.adventure-text-minimessage}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-text-serializer-legacy</artifactId>
|
||||
<version>4.14.0</version>
|
||||
<version>${dependency.adventure-text-serializer-legacy}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Misc -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.10.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>4.11.0</version>
|
||||
<version>2.11.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.unascribed</groupId>
|
||||
<artifactId>flexver-java</artifactId>
|
||||
<version>1.1.0</version>
|
||||
<version>1.1.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>32.1.1-jre</version>
|
||||
<version>33.3.1-jre</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Plugins -->
|
||||
<dependency>
|
||||
<groupId>net.william278</groupId>
|
||||
<artifactId>papiproxybridge</artifactId>
|
||||
<version>1.6.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>eu.kennytv.maintenance</groupId>
|
||||
<artifactId>maintenance-api</artifactId>
|
||||
<version>4.2.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.minecrell</groupId>
|
||||
<artifactId>ServerListPlus</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Test dependencies -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.13.2</version>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.11.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>AdvancedServerList-Core-${plugin.version}${project.build.commit}</finalName>
|
||||
<finalName>AdvancedServerList-Core-${plugin.version}</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<filtering>true</filtering>
|
||||
@@ -127,12 +174,12 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.11.0</version>
|
||||
<version>3.13.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<version>3.6.0</version>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<filters>
|
||||
@@ -140,6 +187,7 @@
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
@@ -149,6 +197,10 @@
|
||||
<pattern>com.unascribed</pattern>
|
||||
<shadedPattern>ch.andre601.advancedserverlist.core.depends.flexver</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>ch.andre601.expressionparser</pattern>
|
||||
<shadedPattern>ch.andre601.advancedserverlist.core.depends.expressionparser</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
<executions>
|
||||
|
||||
@@ -29,14 +29,14 @@ import ch.andre601.advancedserverlist.api.AdvancedServerListAPI;
|
||||
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
|
||||
import ch.andre601.advancedserverlist.core.check.UpdateChecker;
|
||||
import ch.andre601.advancedserverlist.core.commands.CommandHandler;
|
||||
import ch.andre601.advancedserverlist.core.compat.maintenance.MaintenancePlaceholder;
|
||||
import ch.andre601.advancedserverlist.core.file.FileHandler;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
|
||||
import ch.andre601.advancedserverlist.core.objects.ServerPlaceholders;
|
||||
import ch.andre601.advancedserverlist.core.profiles.players.PlayerHandler;
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.ProfileConditionParser;
|
||||
import ch.andre601.advancedserverlist.core.profiles.handlers.PlayerHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
@@ -47,7 +47,8 @@ public class AdvancedServerList<F>{
|
||||
private final FileHandler fileHandler;
|
||||
private final CommandHandler commandHandler;
|
||||
private final PlayerHandler playerHandler;
|
||||
private final AdvancedServerListAPI api = AdvancedServerListAPI.get();
|
||||
private static final AdvancedServerListAPI api = AdvancedServerListAPI.get();
|
||||
private final ProfileConditionParser parser = ProfileConditionParser.create();
|
||||
|
||||
private UpdateChecker updateChecker;
|
||||
|
||||
@@ -59,7 +60,16 @@ public class AdvancedServerList<F>{
|
||||
this.commandHandler = new CommandHandler(this);
|
||||
this.playerHandler = new PlayerHandler(this);
|
||||
|
||||
placeholders.forEach(this.api::addPlaceholderProvider);
|
||||
plugin.getPluginLogger().info("Registering internal Placeholders...");
|
||||
|
||||
placeholders.forEach(this::addPlaceholder);
|
||||
|
||||
if(plugin.isPluginEnabled("Maintenance")){
|
||||
plugin.getPluginLogger().info("Found Maintenance Plugin. Registering extra Placeholder...");
|
||||
addPlaceholder(new MaintenancePlaceholder(this));
|
||||
}
|
||||
|
||||
plugin.getPluginLogger().info("Completed internal Placeholder registration!");
|
||||
|
||||
load();
|
||||
}
|
||||
@@ -68,6 +78,10 @@ public class AdvancedServerList<F>{
|
||||
return new AdvancedServerList<>(plugin, Arrays.asList(placeholders));
|
||||
}
|
||||
|
||||
public static AdvancedServerListAPI getApi(){
|
||||
return api;
|
||||
}
|
||||
|
||||
public PluginCore<F> getPlugin(){
|
||||
return plugin;
|
||||
}
|
||||
@@ -88,8 +102,12 @@ public class AdvancedServerList<F>{
|
||||
return version;
|
||||
}
|
||||
|
||||
public AdvancedServerListAPI getApi(){
|
||||
return api;
|
||||
public ProfileConditionParser getParser(){
|
||||
return parser;
|
||||
}
|
||||
|
||||
public UpdateChecker getUpdateChecker(){
|
||||
return updateChecker;
|
||||
}
|
||||
|
||||
public void disable(){
|
||||
@@ -117,7 +135,7 @@ public class AdvancedServerList<F>{
|
||||
resolveVersion();
|
||||
|
||||
getPlugin().getPluginLogger().info("Starting AdvancedServerList v%s...", version);
|
||||
getPlugin().getPluginLogger().info("Platform: " + plugin.getPlatformName() + " " + plugin.getPlatformVersion());
|
||||
getPlugin().getPluginLogger().info("Platform: %s", plugin.getPlatformInfo());
|
||||
|
||||
if(getFileHandler().loadConfig()){
|
||||
getPlugin().getPluginLogger().info("Successfully loaded config.yml!");
|
||||
@@ -137,15 +155,13 @@ public class AdvancedServerList<F>{
|
||||
}
|
||||
|
||||
if(getFileHandler().loadProfiles()){
|
||||
getPlugin().getPluginLogger().info("Successfully loaded " + getFileHandler().getProfiles().size() + " profiles!");
|
||||
getPlugin().getPluginLogger().info("Successfully loaded %d profile(s)!", getFileHandler().getProfiles().size());
|
||||
}else{
|
||||
getPlugin().getPluginLogger().warn("Unable to load profiles! Check previous lines for errors.");
|
||||
return;
|
||||
}
|
||||
|
||||
Path folder = getPlugin().getFolderPath().resolve("favicons");
|
||||
if(!folder.toFile().exists() && folder.toFile().mkdirs())
|
||||
getPlugin().getPluginLogger().info("Successfully created favicons folder.");
|
||||
getPlugin().loadFaviconHandler(this);
|
||||
|
||||
getPlugin().getPluginLogger().info("Loading Commands...");
|
||||
plugin.loadCommands();
|
||||
@@ -161,6 +177,10 @@ public class AdvancedServerList<F>{
|
||||
getPlugin().getPluginLogger().info("Loading bStats metrics. Disable it in the global config under /plugins/bstats/");
|
||||
plugin.loadMetrics();
|
||||
getPlugin().getPluginLogger().info("Metrics loaded!");
|
||||
|
||||
getPlugin().getPluginLogger().info("Enabling Schedulers...");
|
||||
plugin.startScheduler();
|
||||
getPlugin().getPluginLogger().info("Schedulers enabled.");
|
||||
|
||||
getPlugin().getPluginLogger().info("AdvancedServerList is ready!");
|
||||
|
||||
@@ -189,4 +209,9 @@ public class AdvancedServerList<F>{
|
||||
version = "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
private void addPlaceholder(PlaceholderProvider placeholderProvider){
|
||||
getPlugin().getPluginLogger().info("Registering %s Placeholders...", placeholderProvider.getIdentifier());
|
||||
AdvancedServerList.getApi().addPlaceholderProvider(placeholderProvider);
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,8 @@ import java.util.concurrent.ThreadFactory;
|
||||
public class UpdateCheckThread implements ThreadFactory{
|
||||
@Override
|
||||
public Thread newThread(@NotNull Runnable r){
|
||||
return new Thread(r, "AdvancedServerList Update-Thread");
|
||||
Thread t = new Thread(r, "AdvancedServerList-UpdateThread");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
}
|
||||
}
|
||||
@@ -27,21 +27,23 @@ package ch.andre601.advancedserverlist.core.check;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.objects.CacheUtil;
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.unascribed.flexver.FlexVerComparator;
|
||||
import io.leangen.geantyref.TypeToken;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
@@ -49,66 +51,69 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class UpdateChecker{
|
||||
|
||||
private final String url = "https://api.modrinth.com/v2/project/advancedserverlist/version?loaders=[\"%s\"]";
|
||||
private final OkHttpClient client = new OkHttpClient();
|
||||
|
||||
private final AdvancedServerList core;
|
||||
private final PluginLogger logger;
|
||||
private final String loader;
|
||||
|
||||
private final Type listType = new TypeToken<ArrayList<ModrinthVersion>>(){}.getType();
|
||||
private final Gson gson = new GsonBuilder()
|
||||
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
||||
.setPrettyPrinting()
|
||||
.setLenient()
|
||||
.create();
|
||||
|
||||
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(new UpdateCheckThread());
|
||||
|
||||
public UpdateChecker(AdvancedServerList core){
|
||||
private final CacheUtil<CompletableFuture<ModrinthVersion>> cache = new CacheUtil<>(Duration.ofMinutes(5));
|
||||
|
||||
private final AdvancedServerList<?> core;
|
||||
private final PluginLogger logger;
|
||||
private final String loader;
|
||||
private final HttpClient client;
|
||||
|
||||
public UpdateChecker(AdvancedServerList<?> core){
|
||||
this.core = core;
|
||||
this.logger = core.getPlugin().getPluginLogger();
|
||||
this.loader = core.getPlugin().getLoader();
|
||||
this.client = HttpClient.newBuilder().executor(Executors.newSingleThreadExecutor(new UpdateCheckThread())).build();
|
||||
|
||||
startUpdateChecker();
|
||||
}
|
||||
|
||||
public void startUpdateChecker(){
|
||||
executor.scheduleAtFixedRate(() -> {
|
||||
logger.info("Checking for a new update...");
|
||||
checkUpdate().whenComplete((version, throwable) -> {
|
||||
if(version == null || throwable != null){
|
||||
logger.warn("Failed to look for any updates. See previous messages for reasons.");
|
||||
return;
|
||||
}
|
||||
public void performCachedUpdateCheck(CmdSender sender){
|
||||
CompletableFuture<ModrinthVersion> modrinthVersion = cache.get(this::performUpdateCheck);
|
||||
|
||||
int result = version.compare(core.getVersion());
|
||||
switch(result){
|
||||
case -1 -> printUpdateBanner(version.getVersionNumber(), version.getId(), version.isRelease());
|
||||
case 0 -> logger.info("No new update found. You're running the latest version!");
|
||||
case 1 -> logger.info(
|
||||
"Your version (%s) is higher than the latest release (%s). Are you running a dev build?",
|
||||
core.getVersion(), version.getVersionNumber()
|
||||
);
|
||||
}
|
||||
});
|
||||
}, 0L, 12L, TimeUnit.HOURS);
|
||||
modrinthVersion.whenComplete((version, throwable) -> {
|
||||
if(throwable != null){
|
||||
logger.warn("Encountered an Exception while checking for an update!", throwable);
|
||||
sender.sendErrorMsg("There was an exception while checking for an update. Please check the console for details.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(version == null)
|
||||
return;
|
||||
|
||||
int result = version.compare(core.getVersion());
|
||||
if(result == -1){
|
||||
sender.sendPrefixedMsg("<green>A new Version of AdvancedServerList is available!");
|
||||
sender.sendPrefixedMsg("<green>Your version: <white>%s", core.getVersion());
|
||||
sender.sendPrefixedMsg("<green>Version on Modrinth: <white>%s", version.versionNumber());
|
||||
sender.sendPrefixedMsg(
|
||||
"<click:open_url:'https://modrinth.com/plugin/advancedserverlist/version/%s'>" +
|
||||
"<green>[<white>Download Page</white>]" +
|
||||
"</click>",
|
||||
version.id()
|
||||
);
|
||||
}else
|
||||
if(result == 1){
|
||||
sender.sendPrefixedMsg("You seem to run a newer Version (<white>%</white>) than what is available on Modrinth (<white>%s</white>).", core.getVersion(), version.versionNumber());
|
||||
sender.sendPrefixedMsg("Are you running a development build?");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void disable(){
|
||||
executor.shutdown();
|
||||
client.dispatcher().executorService().shutdown();
|
||||
try{
|
||||
if(!executor.awaitTermination(1, TimeUnit.SECONDS)){
|
||||
executor.shutdownNow();
|
||||
if(!executor.awaitTermination(1, TimeUnit.SECONDS))
|
||||
logger.warn("Scheduler didn't terminate in time!");
|
||||
}
|
||||
|
||||
if(!client.dispatcher().executorService().awaitTermination(1, TimeUnit.SECONDS)){
|
||||
client.dispatcher().executorService().shutdownNow();
|
||||
if(!client.dispatcher().executorService().awaitTermination(1, TimeUnit.SECONDS))
|
||||
logger.warn("OkHttp's Scheduler didn't terminate in time!");
|
||||
logger.warn("Scheduler did not terminate in time!");
|
||||
}
|
||||
}catch(InterruptedException ex){
|
||||
executor.shutdownNow();
|
||||
@@ -116,108 +121,93 @@ public class UpdateChecker{
|
||||
}
|
||||
}
|
||||
|
||||
private CompletableFuture<ModrinthVersion> checkUpdate(){
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
PluginLogger logger = core.getPlugin().getPluginLogger();
|
||||
if(core.getVersion().equals("UNKNOWN")){
|
||||
logger.warn("Cannot perform Update check! Plugin version couldn't be parsed.");
|
||||
return null;
|
||||
}
|
||||
private void startUpdateChecker(){
|
||||
executor.scheduleAtFixedRate(() -> {
|
||||
logger.info("Looking for an update...");
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(String.format(url, loader))
|
||||
performUpdateCheck().whenComplete((version, throwable) -> {
|
||||
if(version == null || throwable != null){
|
||||
logger.warn("Failed to look for any updates. Check previous messages for causes.");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug(UpdateChecker.class, "Comparing versions...");
|
||||
|
||||
int result = version.compare(core.getVersion());
|
||||
switch(result){
|
||||
case -1 -> printUpdateBanner(version);
|
||||
case 0 -> logger.info("You are running the latest version!");
|
||||
case 1 -> logger.info(
|
||||
"Your version (%s) seems to be newer than the latest release (%s). Are you running a dev build?",
|
||||
core.getVersion(), version.versionNumber()
|
||||
);
|
||||
}
|
||||
});
|
||||
}, 0L, 12L, TimeUnit.HOURS);
|
||||
}
|
||||
|
||||
private CompletableFuture<ModrinthVersion> performUpdateCheck(){
|
||||
String finalUrl = String.format(
|
||||
// 'https://api.modrinth.com/v2/project/advancedserverlist/version?loaders=["%s"]' URL encoded.
|
||||
"https://api.modrinth.com/v2/project/advancedserverlist/version?loaders=%%5B%%22%s%%22%%5D",
|
||||
loader
|
||||
);
|
||||
|
||||
logger.debug(UpdateChecker.class, "Checking '%s' for updates...", finalUrl);
|
||||
|
||||
if(core.getVersion().equals("UNKNOWN")){
|
||||
logger.warn("Cannot perform update check. Plugin version is 'UNKNOWN'!");
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
HttpRequest request;
|
||||
try{
|
||||
request = HttpRequest.newBuilder()
|
||||
.uri(new URI(finalUrl))
|
||||
.timeout(Duration.ofSeconds(5))
|
||||
.header("User-Agent", "AdvancedServerList-" + loader + "/" + core.getVersion())
|
||||
.build();
|
||||
|
||||
try(Response response = client.newCall(request).execute()){
|
||||
if(!response.isSuccessful()){
|
||||
logger.warn("Encountered a non-successful response from Modrinth! Code: %d", response.code());
|
||||
return null;
|
||||
}
|
||||
|
||||
ResponseBody body = response.body();
|
||||
if(body == null){
|
||||
logger.warn("Received an empty Response body from Modrinth!");
|
||||
return null;
|
||||
}
|
||||
|
||||
String responseString = body.string();
|
||||
if(responseString.isEmpty()){
|
||||
logger.warn("Received an empty Response body from Modrinth!");
|
||||
return null;
|
||||
}
|
||||
|
||||
List<ModrinthVersion> list = gson.fromJson(responseString, listType);
|
||||
if(list == null || list.isEmpty()){
|
||||
logger.warn("Couldn't convert JSON Array into a valid list.");
|
||||
return null;
|
||||
}
|
||||
|
||||
ModrinthVersion version = list.get(0);
|
||||
|
||||
if(version.getVersionNumber() == null || version.getVersionNumber().isEmpty()){
|
||||
logger.warn("Cannot check latest version. Received version number was null/empty.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return version;
|
||||
}catch(JsonSyntaxException ex){
|
||||
logger.warn("Encountered invalid JSON from Response body!", ex);
|
||||
}catch(URISyntaxException ex){
|
||||
logger.warn("Cannot perform update check. URL '%s' is not a valid URI.", ex, finalUrl);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
return client.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8))
|
||||
.thenApply(response -> new ArrayList<ModrinthVersion>(gson.fromJson(response.body(), listType)))
|
||||
.thenApply(list -> list.get(0))
|
||||
.exceptionally((throwable) -> {
|
||||
logger.warn("Encountered exception while performing update check!", throwable);
|
||||
return null;
|
||||
}catch(IOException ex){
|
||||
logger.warn("Encountered Exception while checking for an update!", ex);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void printUpdateBanner(String version, String versionId, boolean isRelease){
|
||||
logger.info("==================================================================");
|
||||
logger.info("You are running an outdated version of AdvancedServerList!");
|
||||
logger.info("");
|
||||
logger.info("Your version: %s", core.getVersion());
|
||||
logger.info("Modrinth version: %s", version);
|
||||
logger.info("");
|
||||
private void printUpdateBanner(ModrinthVersion version){
|
||||
logger.warn("=======================================================================");
|
||||
logger.warn("You are running an outdated version of AdvancedServerList!");
|
||||
logger.warn("");
|
||||
logger.warn("Your version: %s", core.getVersion());
|
||||
logger.warn("Modrinth: %s", version.versionNumber());
|
||||
logger.warn("");
|
||||
|
||||
if(!isRelease){
|
||||
logger.info("WARNING: This release is an Alpha/Beta! It may contain");
|
||||
logger.info(" breaking changes!");
|
||||
logger.info("");
|
||||
if(!version.isRelease()){
|
||||
logger.warn("WARNING!");
|
||||
logger.warn("This is a %s version and may contain breaking changes and/or bugs!", version.versionType());
|
||||
logger.warn("");
|
||||
}
|
||||
|
||||
logger.info("You can download the latest release from here:");
|
||||
logger.info("https://modrinth.com/plugin/advancedserverlist/version/%s", versionId);
|
||||
logger.info("==================================================================");
|
||||
logger.warn("Download the latest version from here:");
|
||||
logger.warn("https://modrinth.com/plugin/advancedserverlist/version/%s", version.id());
|
||||
logger.warn("=======================================================================");
|
||||
}
|
||||
|
||||
public static class ModrinthVersion{
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
private String id;
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
private String versionNumber;
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
private String versionType;
|
||||
|
||||
public ModrinthVersion(String id, String versionNumber, String versionType){
|
||||
this.id = id;
|
||||
this.versionNumber = versionNumber;
|
||||
this.versionType = versionType;
|
||||
}
|
||||
|
||||
public String getId(){
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getVersionNumber(){
|
||||
return versionNumber;
|
||||
}
|
||||
public record ModrinthVersion(String id, String versionNumber, String versionType){
|
||||
|
||||
public boolean isRelease(){
|
||||
return versionType.equals("release");
|
||||
return versionType().equals("release");
|
||||
}
|
||||
|
||||
public int compare(String version){
|
||||
return FlexVerComparator.compare(version, versionNumber);
|
||||
return FlexVerComparator.compare(version, versionNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,21 +25,43 @@
|
||||
|
||||
package ch.andre601.advancedserverlist.core.commands;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.PluginCommand;
|
||||
import ch.andre601.advancedserverlist.core.migration.minimotd.MiniMOTDConfigMigrator;
|
||||
import ch.andre601.advancedserverlist.core.migration.serverlistplus.SLPConfigMigrator;
|
||||
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
|
||||
import ch.andre601.advancedserverlist.core.profiles.profile.ProfileSerializer;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
import org.spongepowered.configurate.yaml.NodeStyle;
|
||||
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class CommandHandler{
|
||||
|
||||
private final List<PluginCommand> subCommands = new ArrayList<>();
|
||||
private final List<PluginCommand> subCommands;
|
||||
|
||||
public CommandHandler(AdvancedServerList core){
|
||||
subCommands.add(new Help());
|
||||
subCommands.add(new Reload(core));
|
||||
subCommands.add(new ClearCache(core));
|
||||
public CommandHandler(AdvancedServerList<?> core){
|
||||
subCommands = List.of(
|
||||
new Help(core),
|
||||
new Reload(core),
|
||||
new ClearCache(core),
|
||||
new Migrate(core),
|
||||
new Profiles(core)
|
||||
);
|
||||
}
|
||||
|
||||
public List<PluginCommand> getSubCommands(){
|
||||
return subCommands;
|
||||
}
|
||||
|
||||
public void handle(CmdSender sender, String[] args){
|
||||
@@ -49,16 +71,16 @@ public class CommandHandler{
|
||||
}
|
||||
|
||||
for(PluginCommand subCommand : subCommands){
|
||||
if(subCommand.getArgument().equalsIgnoreCase(args[0])){
|
||||
if(!sender.hasPermission(subCommand.getPermission())){
|
||||
if(subCommand.name().equalsIgnoreCase(args[0])){
|
||||
if(!sender.hasPermission(subCommand.permission())){
|
||||
sender.sendErrorMsg(
|
||||
"<red>You do not have the permission <grey>%s</grey> to execute this command.",
|
||||
subCommand.getPermission()
|
||||
subCommand.permission()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
subCommand.handle(sender);
|
||||
subCommand.handle(sender, Arrays.copyOfRange(args, 1, args.length));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -68,34 +90,69 @@ public class CommandHandler{
|
||||
|
||||
private static class Help extends PluginCommand{
|
||||
|
||||
public Help(){
|
||||
private final AdvancedServerList<?> core;
|
||||
|
||||
public Help(AdvancedServerList<?> core){
|
||||
super("help");
|
||||
|
||||
this.core = core;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(CmdSender sender){
|
||||
sender.sendPrefixedMsg("- Commands");
|
||||
sender.sendMsg();
|
||||
sender.sendMsg("<aqua>/asl <white>help <grey>- Shows this help");
|
||||
sender.sendMsg();
|
||||
sender.sendMsg("<aqua>/asl <white>reload <grey>- Reloads the plugin and clears caches");
|
||||
sender.sendMsg();
|
||||
sender.sendMsg("<aqua>/asl <white>clearCache <grey>- Clears the Player and Favicon cache");
|
||||
public void handle(CmdSender sender, String[] args){
|
||||
sender.sendPrefixedMsg("- Commands. Hover for details.");
|
||||
for(PluginCommand command : core.getCommandHandler().getSubCommands()){
|
||||
sender.sendPrefixedMsg("");
|
||||
sender.sendPrefixedMsg(
|
||||
"<aqua><hover:show_text:\"%s\">" +
|
||||
"<click:suggest_command:/asl %s>" +
|
||||
"/asl <white>%s</white>" +
|
||||
"</click>" +
|
||||
"</hover></aqua>",
|
||||
getCommandHover(command),
|
||||
command.name(),
|
||||
command.name()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage(){
|
||||
return "<aqua>/asl <white>help</white></aqua>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description(){
|
||||
return "Shows all available commands.";
|
||||
}
|
||||
|
||||
private String getCommandHover(PluginCommand command){
|
||||
return String.format(
|
||||
"""
|
||||
<grey>Usage: %s
|
||||
Permission: <white>%s</white>
|
||||
|
||||
<white>%s</white>
|
||||
""",
|
||||
command.usage(),
|
||||
command.permission(),
|
||||
command.description()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Reload extends PluginCommand{
|
||||
|
||||
private final AdvancedServerList core;
|
||||
private final AdvancedServerList<?> core;
|
||||
|
||||
public Reload(AdvancedServerList core){
|
||||
public Reload(AdvancedServerList<?> core){
|
||||
super("reload");
|
||||
|
||||
this.core = core;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(CmdSender sender){
|
||||
public void handle(CmdSender sender, String[] args){
|
||||
sender.sendPrefixedMsg("Reloading plugin...");
|
||||
|
||||
if(core.getFileHandler().reloadConfig()){
|
||||
@@ -110,28 +167,32 @@ public class CommandHandler{
|
||||
sender.sendErrorMsg("<red>Error while loading profile(s)!");
|
||||
}
|
||||
|
||||
core.clearFaviconCache();
|
||||
sender.sendPrefixedMsg("<green>Successfully cleared Favicon Cache!");
|
||||
|
||||
core.clearPlayerCache();
|
||||
sender.sendPrefixedMsg("<green>Successfully cleared Player Cache!");
|
||||
|
||||
sender.sendPrefixedMsg("<green>Reload complete!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage(){
|
||||
return "<aqua>/asl <white>reload</white></aqua>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description(){
|
||||
return "Reloads the main config and all profiles.";
|
||||
}
|
||||
}
|
||||
|
||||
private static class ClearCache extends PluginCommand{
|
||||
|
||||
private final AdvancedServerList core;
|
||||
private final AdvancedServerList<?> core;
|
||||
|
||||
public ClearCache(AdvancedServerList core){
|
||||
super("clearCache");
|
||||
public ClearCache(AdvancedServerList<?> core){
|
||||
super("clearcache");
|
||||
|
||||
this.core = core;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(CmdSender sender){
|
||||
public void handle(CmdSender sender, String[] args){
|
||||
sender.sendPrefixedMsg("Clearing caches...");
|
||||
|
||||
core.clearFaviconCache();
|
||||
@@ -142,5 +203,255 @@ public class CommandHandler{
|
||||
|
||||
sender.sendPrefixedMsg("<green>Cache clearing complete!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage(){
|
||||
return "<aqua>/asl <white>clearcache</white></aqua>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description(){
|
||||
return "Clears the Favicon and Player Cache.";
|
||||
}
|
||||
}
|
||||
|
||||
private static class Migrate extends PluginCommand{
|
||||
|
||||
private final AdvancedServerList<?> core;
|
||||
|
||||
public Migrate(AdvancedServerList<?> core){
|
||||
super("migrate");
|
||||
|
||||
this.core = core;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(CmdSender sender, String[] args){
|
||||
if(args.length == 0){
|
||||
sender.sendErrorMsg("<red>Insufficient arguments! Please provide a plugin to migrate from.");
|
||||
sender.sendErrorMsg("<red>Available options:");
|
||||
sender.sendErrorMsg(" - ServerListPlus");
|
||||
sender.sendErrorMsg(" - MiniMOTD");
|
||||
return;
|
||||
}
|
||||
|
||||
if(args[0].equalsIgnoreCase("serverlistplus")){
|
||||
if(!core.getPlugin().isPluginEnabled("ServerListPlus")){
|
||||
sender.sendErrorMsg("<red>Plugin ServerListPlus is not enabled.");
|
||||
sender.sendErrorMsg("<red>It needs to be active for the migration to work!");
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendPrefixedMsg("Migrating ServerListPlus configuration file...");
|
||||
|
||||
int migrated = SLPConfigMigrator.migrate(core, sender);
|
||||
if(migrated == 0){
|
||||
sender.sendErrorMsg("<red>Unable to migrate any ServerListPlus configuration. Check console for details!");
|
||||
}else{
|
||||
sender.sendPrefixedMsg("<green>Migration complete!");
|
||||
}
|
||||
}else
|
||||
if(args[0].equalsIgnoreCase("minimotd")){
|
||||
sender.sendPrefixedMsg("Migrating MiniMOTD configuration file...");
|
||||
|
||||
if(MiniMOTDConfigMigrator.migrate(core, sender)){
|
||||
sender.sendPrefixedMsg("<green>Migration complete!");
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Unable to migrate MiniMOTD configuration. Check console for details!");
|
||||
}
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Unknown plugin <grey>%s</grey>. Available Options:", args[0]);
|
||||
sender.sendErrorMsg(" - ServerListPlus");
|
||||
sender.sendErrorMsg(" - MiniMOTD");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage(){
|
||||
return "<aqua>/asl <white>migrate <grey>\\<</grey>Plugin<grey>></grey></white></aqua>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description(){
|
||||
return "Migrates the configuration from supported Plugins to AdvancedServerList.";
|
||||
}
|
||||
}
|
||||
|
||||
private static class Profiles extends PluginCommand{
|
||||
|
||||
private final AdvancedServerList<?> core;
|
||||
|
||||
public Profiles(AdvancedServerList<?> core){
|
||||
super("profiles");
|
||||
|
||||
this.core = core;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(CmdSender sender, String[] args){
|
||||
if(args.length == 0){
|
||||
sender.sendErrorMsg("<red>Invalid command usage.");
|
||||
sender.sendErrorMsg("<red>Usage:</red> %s", usage());
|
||||
return;
|
||||
}
|
||||
|
||||
switch(args[0].toLowerCase(Locale.ROOT)){
|
||||
case "list" -> {
|
||||
sender.sendPrefixedMsg("Available Profiles:");
|
||||
core.getFileHandler().getProfiles().stream()
|
||||
.sorted(Comparator.comparingInt(ServerListProfile::priority).reversed())
|
||||
.forEach(
|
||||
profile -> sender.sendPrefixedMsg("- <white><hover:show_text:\"%s\">%s</hover></white>", getHover(profile), profile.file())
|
||||
);
|
||||
}
|
||||
case "add" -> {
|
||||
if(args.length == 1){
|
||||
sender.sendErrorMsg("<red>Insufficient arguments.");
|
||||
sender.sendErrorMsg("<red>Usage:</red> /asl profiles add <name>");
|
||||
return;
|
||||
}
|
||||
|
||||
String name = getProfileName(args[1]);
|
||||
|
||||
if(core.getFileHandler().getProfiles().stream().anyMatch(profile -> profile.file().equalsIgnoreCase(name))){
|
||||
sender.sendErrorMsg("<red>A profile with file name</red> %s <red>already exists!", name);
|
||||
return;
|
||||
}
|
||||
|
||||
if(core.getFileHandler().createFile(name)){
|
||||
sender.sendPrefixedMsg("<green>Successfully created</green> %s<green>!", name);
|
||||
sender.sendPrefixedMsg("<green>Load the file using</green> /asl reload");
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Error while trying to create</red> %s<red>!", name);
|
||||
sender.sendErrorMsg("<red>Check console for further details.");
|
||||
}
|
||||
}
|
||||
case "copy" -> {
|
||||
if(args.length < 3){
|
||||
sender.sendErrorMsg("<red>Insufficient arguments.");
|
||||
sender.sendErrorMsg("<red>Usage:</red> /asl profiles copy <profile> <name>");
|
||||
return;
|
||||
}
|
||||
|
||||
String name = getProfileName(args[1]);
|
||||
String copy = getProfileName(args[2]);
|
||||
|
||||
ServerListProfile targetProfile = core.getFileHandler().getProfiles().stream()
|
||||
.filter(file -> file.file().equalsIgnoreCase(name))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
boolean copyExists = core.getFileHandler().getProfiles().stream()
|
||||
.anyMatch(profile -> profile.file().equals(copy));
|
||||
|
||||
if(targetProfile == null){
|
||||
sender.sendErrorMsg("<red>There is no file with name</red> %s<red>!", name);
|
||||
return;
|
||||
}
|
||||
|
||||
if(copyExists){
|
||||
sender.sendErrorMsg("<red>A profile with file name</red> %s <red>already exists!", copy);
|
||||
return;
|
||||
}
|
||||
|
||||
Path profilePath = core.getPlugin().getFolderPath().resolve("profiles").resolve(copy);
|
||||
try{
|
||||
Files.createFile(profilePath);
|
||||
}catch(IOException ex){
|
||||
sender.sendErrorMsg("<red>There was an error while creating the file</red> %s<red>!", copy);
|
||||
sender.sendErrorMsg("<red>Check console for further details.");
|
||||
|
||||
core.getPlugin().getPluginLogger().warn("Encountered IOException while creating file %s", ex, copy);
|
||||
return;
|
||||
}
|
||||
|
||||
YamlConfigurationLoader loader = YamlConfigurationLoader.builder()
|
||||
.path(profilePath)
|
||||
.indent(2)
|
||||
.nodeStyle(NodeStyle.BLOCK)
|
||||
.defaultOptions(options -> options.serializers(builder -> builder.register(ProfileEntry.class, ProfileSerializer.INSTANCE)))
|
||||
.build();
|
||||
|
||||
ConfigurationNode node;
|
||||
try{
|
||||
node = loader.load();
|
||||
}catch(IOException ex){
|
||||
sender.sendErrorMsg("<red>There was an error while loading the file</red> %s<red>!", copy);
|
||||
sender.sendErrorMsg("<red>Check console for further details.");
|
||||
|
||||
core.getPlugin().getPluginLogger().warn("Encountered IOException while loading file %s", ex, copy);
|
||||
return;
|
||||
}
|
||||
|
||||
if(node == null){
|
||||
sender.sendErrorMsg("Cannot copy</red> %s <red>to</red> %s<red>. ConfigurationNode was null.", name, copy);
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
node.node("priority").set(targetProfile.priority());
|
||||
node.node("condition").set(targetProfile.condition());
|
||||
|
||||
node.node("profiles").setList(ProfileEntry.class, targetProfile.profiles());
|
||||
|
||||
node.set(ProfileEntry.class, targetProfile.defaultProfile());
|
||||
}catch(SerializationException ex){
|
||||
sender.sendErrorMsg("<red>There was an Error while copying values from</red> %s <red>to</red> %s<red>.", name, copy);
|
||||
sender.sendErrorMsg("<red>Check console for details.");
|
||||
|
||||
core.getPlugin().getPluginLogger().warn("Encountered SerializationException while set values for %s!", ex, copy);
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
loader.save(node);
|
||||
sender.sendPrefixedMsg("<green>Successfully created copy of</green> %s <green>as</green> %s<green>!", name, copy);
|
||||
sender.sendPrefixedMsg("<green>Load the file using</green> /asl reload");
|
||||
}catch(IOException ex){
|
||||
sender.sendErrorMsg("<red>There was an Error while trying to save</red> %s<red>!", copy);
|
||||
sender.sendErrorMsg("<red>Check console for details.");
|
||||
|
||||
core.getPlugin().getPluginLogger().warn("Encountered IOException while saving ConfigurationNode for %s!", ex, copy);
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
sender.sendErrorMsg("<red>Unknown argument</red> %s<red>!", args[0]);
|
||||
sender.sendErrorMsg("<red>Usage:</red> %s", usage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage(){
|
||||
return "<aqua>/asl <white>profiles</white> <grey>\\<" +
|
||||
"<white>add</white> \\<<white>name</white>> | " +
|
||||
"<white>copy</white> \\<<white>profile</white>> \\<<white>name</white>> | " +
|
||||
"<white>list</white>" +
|
||||
"></grey></aqua>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description(){
|
||||
return "Creates a new profile, copies one from an existing file, or lists all available profiles.";
|
||||
}
|
||||
|
||||
private String getHover(ServerListProfile profile){
|
||||
return String.format(
|
||||
"""
|
||||
<grey>Priority: <white>%s</white>
|
||||
Condition: <white>%s</white>
|
||||
|
||||
Valid profile: %s
|
||||
""",
|
||||
profile.priority(),
|
||||
profile.condition() == null || profile.condition().isEmpty() ? "<i>None</i>" : profile.condition(),
|
||||
profile.isInvalidProfile() ? "<red>No</red>" : "<green>Yes</green>"
|
||||
);
|
||||
}
|
||||
|
||||
private String getProfileName(String name){
|
||||
String fileName = name.toLowerCase(Locale.ROOT);
|
||||
|
||||
return fileName.endsWith(".yml") ? fileName : fileName + ".yml";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package ch.andre601.advancedserverlist.core.compat.maintenance;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
|
||||
public class MaintenancePlaceholder extends PlaceholderProvider{
|
||||
|
||||
private final AdvancedServerList<?> plugin;
|
||||
|
||||
public MaintenancePlaceholder(AdvancedServerList<?> plugin){
|
||||
super("maintenance");
|
||||
|
||||
this.plugin = plugin;
|
||||
}
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
|
||||
if(!placeholder.equals("maintenanceEnabled") || !plugin.getPlugin().isPluginEnabled("Maintenance"))
|
||||
return null;
|
||||
|
||||
return String.valueOf(MaintenanceUtil.get().isMaintenanceEnabled());
|
||||
}
|
||||
}
|
||||
@@ -23,34 +23,29 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.objects;
|
||||
package ch.andre601.advancedserverlist.core.compat.maintenance;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import eu.kennytv.maintenance.api.Maintenance;
|
||||
import eu.kennytv.maintenance.api.MaintenanceProvider;
|
||||
|
||||
public class GenericServerImpl implements GenericServer{
|
||||
public class MaintenanceUtil{
|
||||
|
||||
private final int online;
|
||||
private final int max;
|
||||
private final String host;
|
||||
private static MaintenanceUtil instance;
|
||||
|
||||
public GenericServerImpl(int online, int max, String host){
|
||||
this.online = online;
|
||||
this.max = max;
|
||||
this.host = host;
|
||||
private final Maintenance api;
|
||||
|
||||
private MaintenanceUtil(){
|
||||
this.api = MaintenanceProvider.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayersOnline(){
|
||||
return online;
|
||||
public static MaintenanceUtil get(){
|
||||
if(instance != null)
|
||||
return instance;
|
||||
|
||||
return (instance = new MaintenanceUtil());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayersMax(){
|
||||
return max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHost(){
|
||||
return host;
|
||||
public boolean isMaintenanceEnabled(){
|
||||
return api.isMaintenance();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.compat.papi;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.objects.CacheUtil;
|
||||
import net.william278.papiproxybridge.api.PlaceholderAPI;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CompletionException;
|
||||
|
||||
public class PAPIUtil{
|
||||
|
||||
private static PAPIUtil instance;
|
||||
|
||||
private final CacheUtil<String> cache = new CacheUtil<>(Duration.ofSeconds(5));
|
||||
private final PlaceholderAPI papi;
|
||||
|
||||
private PAPIUtil(){
|
||||
this.papi = PlaceholderAPI.createInstance();
|
||||
}
|
||||
|
||||
public static PAPIUtil get(){
|
||||
if(instance != null)
|
||||
return instance;
|
||||
|
||||
return (instance = new PAPIUtil());
|
||||
}
|
||||
|
||||
// Make sure the version of PAPIProxyBridge we get has the required methods we need...
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
public boolean isCompatible(){
|
||||
try{
|
||||
papi.getClass().getMethod("getServers");
|
||||
return true;
|
||||
}catch(NoSuchMethodException ex){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public String getServer(){
|
||||
return cache.get(() -> {
|
||||
List<String> servers;
|
||||
|
||||
try{
|
||||
Set<String> value = papi.getServers().getNow(null);
|
||||
if(value == null)
|
||||
return null;
|
||||
|
||||
servers = List.copyOf(value);
|
||||
}catch(CancellationException | CompletionException ex){
|
||||
return null;
|
||||
}
|
||||
|
||||
if(servers == null || servers.isEmpty())
|
||||
return null;
|
||||
|
||||
return servers.get(0);
|
||||
});
|
||||
}
|
||||
|
||||
public <P> P getPlayer(Collection<P> players){
|
||||
if(players.isEmpty())
|
||||
return null;
|
||||
|
||||
return List.copyOf(players).get(0);
|
||||
}
|
||||
|
||||
public String parse(String text, UUID carrier, UUID player){
|
||||
try{
|
||||
return papi.formatPlaceholders(text, carrier, player).getNow(text);
|
||||
}catch(IllegalArgumentException | CancellationException | CompletionException ex){
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,9 +29,9 @@ import ch.andre601.advancedserverlist.api.events.GenericServerListEvent;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.events.GenericEventWrapper;
|
||||
import ch.andre601.advancedserverlist.core.objects.GenericServerImpl;
|
||||
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
|
||||
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
|
||||
import ch.andre601.advancedserverlist.core.profiles.profile.ProfileManager;
|
||||
@@ -39,12 +39,18 @@ import ch.andre601.advancedserverlist.core.profiles.replacer.StringReplacer;
|
||||
|
||||
public class PingEventHandler{
|
||||
|
||||
public static <F, P extends GenericPlayer> void handleEvent(GenericEventWrapper<F, P> event){
|
||||
if(event.isInvalidProtocol())
|
||||
return;
|
||||
public static <F, P extends GenericPlayer> ProfileEntry handleEvent(GenericEventWrapper<F, P> event){
|
||||
event.getPlugin().getPluginLogger().debug(PingEventHandler.class, "Received ping event. Handling it...");
|
||||
if(event.isInvalidProtocol()){
|
||||
event.getPlugin().getPluginLogger().debug(PingEventHandler.class, "Not handling event. Protocol was invalid.");
|
||||
return null;
|
||||
}
|
||||
|
||||
PluginCore<F> plugin = event.getPlugin();
|
||||
String host = event.getVirtualHost();
|
||||
PluginLogger logger = plugin.getPluginLogger();
|
||||
|
||||
logger.debug(PingEventHandler.class, "Protocol valid. Continue handling...");
|
||||
|
||||
int online = event.getOnlinePlayers();
|
||||
int max = event.getMaxPlayers();
|
||||
@@ -57,25 +63,83 @@ public class PingEventHandler{
|
||||
|
||||
ServerListProfile profile = ProfileManager.resolveProfile(plugin.getCore(), player, server);
|
||||
|
||||
if(profile == null)
|
||||
return;
|
||||
if(profile == null){
|
||||
logger.debugWarn(PingEventHandler.class, "Server List Profile couldn't be resolved properly. Cancelling event handling...");
|
||||
return null;
|
||||
}
|
||||
|
||||
GenericServerListEvent e = event.callEvent(ProfileManager.merge(profile));
|
||||
if(e == null || e.isCancelled())
|
||||
return;
|
||||
logger.debug(PingEventHandler.class, "Received valid Server List Profile. Calling PreServerListSetEvent...");
|
||||
|
||||
ProfileEntry entry = e.getEntry();
|
||||
if(entry.isInvalid())
|
||||
return;
|
||||
GenericServerListEvent slEvent = event.callEvent(ProfileManager.merge(profile));
|
||||
if(slEvent == null || slEvent.isCancelled()){
|
||||
logger.debug(PingEventHandler.class, "PreServerListSetEvent was cancelled. Stopping ping handling...");
|
||||
return null;
|
||||
}
|
||||
|
||||
if(ProfileManager.checkOption(entry.extraPlayersEnabled())){
|
||||
max = online + (entry.extraPlayersCount() == null ? 0 : entry.extraPlayersCount());
|
||||
event.setMaxPlayers(max);
|
||||
logger.debug(PingEventHandler.class, "PreServerListSetEvent completed. Proceeding with ping handling...");
|
||||
|
||||
ProfileEntry entry = slEvent.getEntry();
|
||||
if(entry.isInvalid()){
|
||||
logger.debugWarn(PingEventHandler.class, "No valid ProfileEntry retrieved. Cancelling ping handling...");
|
||||
return null;
|
||||
}
|
||||
|
||||
if(ProfileManager.checkOption(entry.onlinePlayersEnabled()) && ignoreMaintenance(event, "onlinePlayers")){
|
||||
logger.debug(PingEventHandler.class, "'playerCount -> onlinePlayers -> enabled' option is true. Trying to apply '%s'...", entry.onlinePlayersCount());
|
||||
String onlinePlayers = ComponentParser.text(entry.onlinePlayersCount())
|
||||
.modifyText(text -> StringReplacer.replace(text, player, server))
|
||||
.modifyText(text -> event.parsePAPIPlaceholders(text, player))
|
||||
.toString();
|
||||
|
||||
try{
|
||||
online = onlinePlayers == null ? online : Integer.parseInt(onlinePlayers);
|
||||
event.setOnlinePlayers(online);
|
||||
}catch(NumberFormatException ex){
|
||||
logger.warn("Option 'playerCount -> onlinePlayers -> amount' is not a valid Number!");
|
||||
}
|
||||
}
|
||||
|
||||
boolean extraPlayersEnabled = ProfileManager.checkOption(entry.extraPlayersEnabled());
|
||||
|
||||
if(extraPlayersEnabled && ignoreMaintenance(event, "extraPlayers")){
|
||||
logger.debug(PingEventHandler.class, "'playerCount -> extraPlayers -> enabled' option is true. Trying to apply '%s'...", entry.extraPlayersCount());
|
||||
String extraPlayers = ComponentParser.text(entry.extraPlayersCount())
|
||||
.modifyText(text -> StringReplacer.replace(text, player, server))
|
||||
.modifyText(text -> event.parsePAPIPlaceholders(text, player))
|
||||
.toString();
|
||||
|
||||
try{
|
||||
max = online + (extraPlayers == null ? 0 : Integer.parseInt(extraPlayers));
|
||||
|
||||
logger.debug(PingEventHandler.class, "'playerCount -> extraPlayers -> amount' is a valid number. Applying it...");
|
||||
event.setMaxPlayers(max);
|
||||
}catch(NumberFormatException ex){
|
||||
logger.warn("Option 'playerCount -> extraPlayers -> amount' is not a valid Number!");
|
||||
}
|
||||
}
|
||||
|
||||
if(ProfileManager.checkOption(entry.maxPlayersEnabled()) && !extraPlayersEnabled && ignoreMaintenance(event, "maxPlayers")){
|
||||
logger.debug(PingEventHandler.class, "'playerCount -> maxPlayers -> enabled' option is true. Trying to apply '%s'...", entry.maxPlayersCount());
|
||||
String maxPlayers = ComponentParser.text(entry.maxPlayersCount())
|
||||
.modifyText(text -> StringReplacer.replace(text, player, server))
|
||||
.modifyText(text -> event.parsePAPIPlaceholders(text, player))
|
||||
.toString();
|
||||
|
||||
try{
|
||||
max = maxPlayers == null ? 0 : Integer.parseInt(maxPlayers);
|
||||
|
||||
logger.debug(PingEventHandler.class, "'playerCount -> maxPlayers -> amount' is a valid number. Applying it...");
|
||||
event.setMaxPlayers(max);
|
||||
}catch(NumberFormatException ex){
|
||||
logger.warn("Option 'playerCount -> maxPlayers -> amount' is not a valid Number!");
|
||||
}
|
||||
}
|
||||
|
||||
GenericServer finalServer = event.createGenericServer(online, max, host);
|
||||
|
||||
if(ProfileManager.checkOption(entry.motd())){
|
||||
if(ProfileManager.checkOption(entry.motd()) && ignoreMaintenance(event, "motd")){
|
||||
logger.debug(PingEventHandler.class, "'motd' option present. Applying '%s'...", String.join("\\n", entry.motd()));
|
||||
|
||||
event.setMotd(
|
||||
ComponentParser.list(entry.motd())
|
||||
.modifyText(text -> StringReplacer.replace(text, player, finalServer))
|
||||
@@ -86,11 +150,15 @@ public class PingEventHandler{
|
||||
|
||||
boolean hidePlayers = ProfileManager.checkOption(entry.hidePlayersEnabled());
|
||||
|
||||
if(hidePlayers){
|
||||
if(hidePlayers && ignoreMaintenance(event, "hidePlayers")){
|
||||
logger.debug(PingEventHandler.class, "'playerCount -> hidePlayers' enabled. Hiding player count...");
|
||||
|
||||
event.hidePlayers();
|
||||
}
|
||||
|
||||
if(ProfileManager.checkOption(entry.playerCountText()) && !hidePlayers){
|
||||
if(ProfileManager.checkOption(entry.playerCountText()) && !hidePlayers && ignoreMaintenance(event, "playerCountText")){
|
||||
logger.debug(PingEventHandler.class, "'playerCount -> text' option set. Applying '%s'...", entry.playerCountText());
|
||||
|
||||
event.setPlayerCount(
|
||||
ComponentParser.text(entry.playerCountText())
|
||||
.modifyText(text -> StringReplacer.replace(text, player, finalServer))
|
||||
@@ -99,29 +167,40 @@ public class PingEventHandler{
|
||||
);
|
||||
}
|
||||
|
||||
if(ProfileManager.checkOption(entry.players()) && !hidePlayers){
|
||||
if(ProfileManager.checkOption(entry.players()) && !hidePlayers && ignoreMaintenance(event, "playerCountHover")){
|
||||
logger.debug(PingEventHandler.class, "'playerCount -> hover' option set. Applying '%s'...", String.join("\\n", entry.players()));
|
||||
|
||||
event.setPlayers(entry.players(), player, server);
|
||||
}
|
||||
|
||||
if(ProfileManager.checkOption(entry.favicon())){
|
||||
String favicon = StringReplacer.replace(entry.favicon(), player, server);
|
||||
if(ProfileManager.checkOption(entry.favicon()) && ignoreMaintenance(event, "favicon")){
|
||||
logger.debug(PingEventHandler.class, "'favicon' option set. Resolving '%s'...", entry.favicon());
|
||||
|
||||
F fav = plugin.getFaviconHandler().getFavicon(favicon, image -> {
|
||||
try{
|
||||
return event.createFavicon(image);
|
||||
}catch(Exception ex){
|
||||
plugin.getPluginLogger().warn("Encountered an Exception while creating Favicon!", ex);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
String faviconString = StringReplacer.replace(entry.favicon(), player, server);
|
||||
|
||||
if(fav == null){
|
||||
F favicon = plugin.getFaviconHandler().getFavicon(faviconString);
|
||||
|
||||
if(favicon == null){
|
||||
logger.debugWarn(PingEventHandler.class, "Favicon was invalid or not yet resolved! Using default favicon of Server/Proxy...");
|
||||
|
||||
event.setDefaultFavicon();
|
||||
}else{
|
||||
event.setFavicon(fav);
|
||||
logger.debug(PingEventHandler.class, "Applying favicon...");
|
||||
|
||||
event.setFavicon(favicon);
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug(PingEventHandler.class, "Event handling completed. Updating Ping data...");
|
||||
event.updateEvent();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
private static <F, P extends GenericPlayer> boolean ignoreMaintenance(GenericEventWrapper<F, P> event, String option){
|
||||
if(!event.isMaintenanceModeActive())
|
||||
return true;
|
||||
|
||||
return !event.getPlugin().getCore().getFileHandler().getBool(true, "disableDuringMaintenance", option);
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
package ch.andre601.advancedserverlist.core.file;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
import org.spongepowered.configurate.ConfigurateException;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
@@ -32,16 +33,70 @@ import org.spongepowered.configurate.NodePath;
|
||||
import org.spongepowered.configurate.transformation.ConfigurationTransformation;
|
||||
import org.spongepowered.configurate.transformation.TransformAction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
public class ConfigMigrator{
|
||||
|
||||
public static final int LATEST = 1;
|
||||
public static final int LATEST = resolveConfigVersion();
|
||||
|
||||
private ConfigMigrator(){}
|
||||
|
||||
public static ConfigurationTransformation.Versioned create(){
|
||||
return ConfigurationTransformation.versionedBuilder()
|
||||
.versionKey("configVersion")
|
||||
.addVersion(LATEST, oneToTwo())
|
||||
.addVersion(LATEST, fiveToSix())
|
||||
.addVersion(5, fourToFive())
|
||||
.addVersion(4, threeToFour())
|
||||
.addVersion(3, twoToThree())
|
||||
.addVersion(2, oneToTwo())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static ConfigurationTransformation fiveToSix(){
|
||||
return ConfigurationTransformation.builder()
|
||||
.addAction(NodePath.path("disableDuringMaintenance"), (path, value) -> {
|
||||
value.node("onlinePlayers").set(true);
|
||||
|
||||
return null;
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
public static ConfigurationTransformation fourToFive(){
|
||||
return ConfigurationTransformation.builder()
|
||||
.addAction(NodePath.path(), (path, value) -> {
|
||||
value.node("disableDuringMaintenance", "motd").set(true);
|
||||
value.node("disableDuringMaintenance", "favicon").set(true);
|
||||
value.node("disableDuringMaintenance", "hidePlayers").set(true);
|
||||
value.node("disableDuringMaintenance", "playerCountText").set(true);
|
||||
value.node("disableDuringMaintenance", "playerCountHover").set(true);
|
||||
value.node("disableDuringMaintenance", "extraPlayers").set(true);
|
||||
value.node("disableDuringMaintenance", "maxPlayers").set(true);
|
||||
|
||||
return null;
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
public static ConfigurationTransformation threeToFour(){
|
||||
return ConfigurationTransformation.builder()
|
||||
.addAction(NodePath.path(), (path, value) -> {
|
||||
value.node("faviconCacheTime").set(1);
|
||||
|
||||
return null;
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
public static ConfigurationTransformation twoToThree(){
|
||||
return ConfigurationTransformation.builder()
|
||||
.addAction(NodePath.path(), (path, value) -> {
|
||||
value.node("debug").set(false);
|
||||
|
||||
return null;
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -67,9 +122,21 @@ public class ConfigMigrator{
|
||||
versioned.apply(node);
|
||||
final int endVersion = versioned.version(node);
|
||||
if(startVersion < endVersion)
|
||||
logger.info("Migrated config.yml from " + startVersion + " to " + endVersion);
|
||||
logger.info("Migrated config.yml from v" + startVersion + " to v" + endVersion);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private static int resolveConfigVersion(){
|
||||
try(InputStream is = AdvancedServerList.class.getResourceAsStream("/version.properties")){
|
||||
Properties properties = new Properties();
|
||||
|
||||
properties.load(is);
|
||||
|
||||
return Integer.parseInt(properties.getProperty("config-version"));
|
||||
}catch(IOException | NumberFormatException ex){
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,10 +25,10 @@
|
||||
|
||||
package ch.andre601.advancedserverlist.core.file;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
|
||||
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
|
||||
import ch.andre601.advancedserverlist.core.profiles.profile.ProfileSerializer;
|
||||
import org.spongepowered.configurate.ConfigurateException;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
@@ -99,23 +99,36 @@ public class FileHandler{
|
||||
|
||||
public boolean loadProfiles(){
|
||||
logger.info("Loading profiles...");
|
||||
if(!profilesFolder.toFile().exists() && profilesFolder.toFile().mkdirs()){
|
||||
|
||||
if(createFile("default.yml")){
|
||||
return reloadProfiles();
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean createFile(String name){
|
||||
if(Files.notExists(profilesFolder) && profilesFolder.toFile().mkdirs()){
|
||||
logger.info("Successfully created profiles folder.");
|
||||
|
||||
try(InputStream stream = plugin.getClass().getResourceAsStream("/profiles/default.yml")){
|
||||
if(stream == null){
|
||||
logger.warn("Cannot retrieve default.yml from Plugin.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Files.copy(stream, profilesFolder.resolve("default.yml"));
|
||||
}catch(IOException ex){
|
||||
logger.warn("Cannot create default.yml for plugin.", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return reloadProfiles();
|
||||
File file = new File(profilesFolder.toFile(), name);
|
||||
if(file.exists()){
|
||||
return true;
|
||||
}
|
||||
|
||||
try(InputStream stream = plugin.getClass().getResourceAsStream("/profiles/default.yml")){
|
||||
if(stream == null){
|
||||
logger.warn("Cannot retrieve content of internal default.yml file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Files.copy(stream, profilesFolder.resolve(name));
|
||||
return true;
|
||||
}catch(IOException ex){
|
||||
logger.warn("Encountered IOException while trying to create file %s", ex, name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean migrateConfig(){
|
||||
@@ -157,7 +170,7 @@ public class FileHandler{
|
||||
continue;
|
||||
|
||||
profiles.add(ServerListProfile.Builder.resolve(file.getName(), tmp, logger).build());
|
||||
logger.info("Loaded " + file.getName());
|
||||
logger.info("Loaded '%s'!", file.getName());
|
||||
}
|
||||
|
||||
if(profiles.isEmpty()){
|
||||
@@ -165,7 +178,7 @@ public class FileHandler{
|
||||
return false;
|
||||
}
|
||||
|
||||
profiles.sort(Comparator.comparing(ServerListProfile::getPriority).reversed());
|
||||
profiles.sort(Comparator.comparing(ServerListProfile::priority).reversed());
|
||||
return !profiles.isEmpty();
|
||||
}
|
||||
|
||||
@@ -178,7 +191,7 @@ public class FileHandler{
|
||||
try{
|
||||
return loader.load();
|
||||
}catch(IOException ex){
|
||||
logger.warn("Cannot load " + path.toFile().getName() + " due to an IOException!", ex);
|
||||
logger.warn("Cannot load '%s' due to an IOException!", ex, path.toFile().getName());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -187,8 +200,17 @@ public class FileHandler{
|
||||
return node.node(path).getString(def);
|
||||
}
|
||||
|
||||
public boolean getBool(boolean def, Object... path){
|
||||
return node.node(path).getBoolean(def);
|
||||
}
|
||||
|
||||
public boolean getBoolean(Object... path){
|
||||
return node.node(path).getBoolean();
|
||||
return getBool(false, path);
|
||||
}
|
||||
|
||||
public long getLong(long def, long limit, Object... path){
|
||||
long value = node.node(path).getLong(def);
|
||||
return Math.max(value, limit);
|
||||
}
|
||||
|
||||
public boolean isOldConfig(){
|
||||
@@ -214,7 +236,7 @@ public class FileHandler{
|
||||
}
|
||||
|
||||
Files.copy(config, configBackup.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
logger.info("Saved backup as '" + configBackup.getName() + "'!");
|
||||
logger.info("Saved backup as '%s'!", configBackup.getName());
|
||||
|
||||
return true;
|
||||
}catch(IOException ex){
|
||||
|
||||
@@ -27,6 +27,10 @@ package ch.andre601.advancedserverlist.core.interfaces;
|
||||
|
||||
public interface PluginLogger{
|
||||
|
||||
void debug(Class<?> clazz, String msg, Object... args);
|
||||
|
||||
void debugWarn(Class<?> clazz, String msg, Object... args);
|
||||
|
||||
void info(String msg, Object... args);
|
||||
|
||||
void warn(String msg, Object... args);
|
||||
|
||||
@@ -29,19 +29,23 @@ import java.util.Locale;
|
||||
|
||||
public abstract class PluginCommand{
|
||||
|
||||
private final String argument;
|
||||
private final String name;
|
||||
|
||||
public PluginCommand(String argument){
|
||||
this.argument = argument;
|
||||
public PluginCommand(String name){
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getArgument(){
|
||||
return argument;
|
||||
public String name(){
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getPermission(){
|
||||
return "advancedserverlist.command." + getArgument().toLowerCase(Locale.ROOT);
|
||||
public String permission(){
|
||||
return "advancedserverlist.command." + name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
public abstract void handle(CmdSender sender);
|
||||
public abstract void handle(CmdSender sender, String[] args);
|
||||
|
||||
public abstract String usage();
|
||||
|
||||
public abstract String description();
|
||||
}
|
||||
@@ -27,8 +27,9 @@ package ch.andre601.advancedserverlist.core.interfaces.core;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler;
|
||||
import ch.andre601.advancedserverlist.core.profiles.handlers.FaviconHandler;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public interface PluginCore<F>{
|
||||
@@ -39,8 +40,14 @@ public interface PluginCore<F>{
|
||||
|
||||
void loadMetrics();
|
||||
|
||||
void loadFaviconHandler(AdvancedServerList<F> core);
|
||||
|
||||
void clearFaviconCache();
|
||||
|
||||
void downloadLibrary(String groupId, String artifactId, String version);
|
||||
|
||||
void startScheduler();
|
||||
|
||||
AdvancedServerList<F> getCore();
|
||||
|
||||
Path getFolderPath();
|
||||
@@ -49,9 +56,18 @@ public interface PluginCore<F>{
|
||||
|
||||
FaviconHandler<F> getFaviconHandler();
|
||||
|
||||
String getPlatformName();
|
||||
|
||||
String getPlatformVersion();
|
||||
String getPlatformInfo();
|
||||
|
||||
String getLoader();
|
||||
|
||||
boolean isPluginEnabled(String plugin);
|
||||
|
||||
F createFavicon(BufferedImage image) throws Exception;
|
||||
|
||||
default boolean isDebugEnabled(){
|
||||
if(getCore() == null)
|
||||
return false;
|
||||
|
||||
return getCore().getFileHandler().getBoolean("debug");
|
||||
}
|
||||
}
|
||||
@@ -25,15 +25,14 @@
|
||||
|
||||
package ch.andre601.advancedserverlist.core.interfaces.events;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.events.GenericServerListEvent;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
|
||||
import ch.andre601.advancedserverlist.api.events.GenericServerListEvent;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
|
||||
import ch.andre601.advancedserverlist.core.objects.CachedPlayer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
@@ -43,6 +42,8 @@ public interface GenericEventWrapper<F, P extends GenericPlayer>{
|
||||
|
||||
void setMaxPlayers(int maxPlayers);
|
||||
|
||||
void setOnlinePlayers(int onlinePlayers);
|
||||
|
||||
void setMotd(Component component);
|
||||
|
||||
void hidePlayers();
|
||||
@@ -59,6 +60,8 @@ public interface GenericEventWrapper<F, P extends GenericPlayer>{
|
||||
|
||||
boolean isInvalidProtocol();
|
||||
|
||||
boolean isMaintenanceModeActive();
|
||||
|
||||
int getProtocolVersion();
|
||||
|
||||
int getOnlinePlayers();
|
||||
@@ -77,8 +80,6 @@ public interface GenericEventWrapper<F, P extends GenericPlayer>{
|
||||
|
||||
GenericServer createGenericServer(int playersOnline, int playersMax, String host);
|
||||
|
||||
F createFavicon(BufferedImage image) throws Exception;
|
||||
|
||||
default String resolveHost(InetSocketAddress address){
|
||||
return address == null ? null : address.getHostString();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.migration.minimotd;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.NullBool;
|
||||
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.migration.minimotd.serializing.MiniMOTDConfig;
|
||||
import ch.andre601.advancedserverlist.core.migration.minimotd.serializing.MiniMOTDSerializer;
|
||||
import ch.andre601.advancedserverlist.core.profiles.profile.ProfileSerializer;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.hocon.HoconConfigurationLoader;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
import org.spongepowered.configurate.yaml.NodeStyle;
|
||||
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class MiniMOTDConfigMigrator{
|
||||
|
||||
public static boolean migrate(AdvancedServerList<?> core, CmdSender sender){
|
||||
core.getPlugin().downloadLibrary("org.spongepowered", "configurate-hocon", "4.1.2");
|
||||
|
||||
PluginLogger logger = core.getPlugin().getPluginLogger();
|
||||
String folder = core.getPlugin().getLoader().equals("velocity") ? "minimotd-velocity" : "MiniMOTD";
|
||||
|
||||
Path mainConf = core.getPlugin().getFolderPath().getParent().resolve(folder).resolve("main.conf");
|
||||
if(!Files.exists(mainConf)){
|
||||
logger.warn("[Migrator - MiniMOTD] Cannot find a main.conf file in /plugins/%s/ folder.", folder);
|
||||
sender.sendErrorMsg(" -> <red>No main.conf file found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
HoconConfigurationLoader loader = HoconConfigurationLoader.builder()
|
||||
.path(mainConf)
|
||||
.defaultOptions(options -> options.serializers(builder ->
|
||||
builder.register(MiniMOTDConfig.class, MiniMOTDSerializer.MINI_MOTD_SERIALIZER)
|
||||
.register(MiniMOTDConfig.Motd.class, MiniMOTDSerializer.MOTD_SERIALIZER)
|
||||
.register(MiniMOTDConfig.PlayerCount.class, MiniMOTDSerializer.PLAYER_COUNT_SERIALIZER)
|
||||
))
|
||||
.build();
|
||||
|
||||
MiniMOTDConfig config;
|
||||
try{
|
||||
ConfigurationNode node = loader.load();
|
||||
|
||||
config = node.get(MiniMOTDConfig.class);
|
||||
}catch(IOException ex){
|
||||
logger.warn("[Migrator - MiniMOTD] Encountered IOException while loading main.conf file.", ex);
|
||||
sender.sendErrorMsg(" -> <red>IOException while loading</red> main.conf <red>file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(config == null){
|
||||
logger.warn("[Migrator - MiniMOTD] Retrieved main.conf file returned null.");
|
||||
sender.sendErrorMsg(" -> <red>Received invalid</red> main.conf <red>file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
ProfileEntry.Builder profileBuilder = ProfileEntry.empty().builder();
|
||||
List<ProfileEntry.Builder> profilesBuilders = new ArrayList<>();
|
||||
|
||||
if(config.motdEnabled() && config.motds().size() > 1){
|
||||
for(int i = 0; i < config.motds().size(); i++){
|
||||
profilesBuilders.add(ProfileEntry.empty().builder());
|
||||
}
|
||||
}
|
||||
|
||||
if(config.motdEnabled()){
|
||||
if(config.motds().size() == 1){
|
||||
applyMotd(profileBuilder, config.motds().get(0));
|
||||
}else
|
||||
if(config.motds().size() > 1){
|
||||
for(int i = 0; i < config.motds().size(); i++){
|
||||
applyMotd(profilesBuilders.get(i), config.motds().get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(config.iconEnabled()){
|
||||
if(config.motds().size() == 1){
|
||||
MiniMOTDConfig.Motd motd = config.motds().get(0);
|
||||
if(!motd.icon().isEmpty() && !motd.icon().equalsIgnoreCase("random")){
|
||||
profileBuilder.favicon(motd.icon());
|
||||
}
|
||||
}else
|
||||
if(config.motds().size() > 1){
|
||||
for(int i = 0; i < config.motds().size(); i++){
|
||||
MiniMOTDConfig.Motd motd = config.motds().get(i);
|
||||
if(!motd.icon().isEmpty() && !motd.icon().equalsIgnoreCase("random")){
|
||||
profilesBuilders.get(i).favicon(motd.icon());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(config.playerCount() != null){
|
||||
MiniMOTDConfig.PlayerCount playerCount = config.playerCount();
|
||||
|
||||
NullBool hidePlayers = NullBool.resolve(playerCount.hidePlayers());
|
||||
NullBool xMoreEnabled = NullBool.resolve(playerCount.xMoreEnabled());
|
||||
int xMore = playerCount.xMore();
|
||||
NullBool maxPlayersEnabled = NullBool.resolve(playerCount.maxPlayersEnabled());
|
||||
int maxPlayers = playerCount.maxPlayers();
|
||||
|
||||
profileBuilder.hidePlayersEnabled(hidePlayers)
|
||||
.extraPlayersEnabled(xMoreEnabled)
|
||||
.extraPlayersCount(String.valueOf(xMore))
|
||||
.maxPlayersEnabled(maxPlayersEnabled)
|
||||
.maxPlayersCount(String.valueOf(maxPlayers));
|
||||
}
|
||||
|
||||
ProfileEntry entry = profileBuilder.build();
|
||||
List<ProfileEntry> profiles = profilesBuilders.stream()
|
||||
.map(ProfileEntry.Builder::build)
|
||||
.toList();
|
||||
|
||||
boolean profilesInvalid = profiles.isEmpty() || profiles.stream().allMatch(ProfileEntry::isInvalid);
|
||||
if(entry.isInvalid() && profilesInvalid){
|
||||
logger.warn("[Migrator - MiniMOTD] Main ProfileEntry was invalid and profiles were empty.");
|
||||
sender.sendErrorMsg(" -> <red>Received invalid configuration.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Path profile = core.getPlugin().getFolderPath().resolve("profiles").resolve("minimotd_migrated.yml");
|
||||
if(Files.exists(profile)){
|
||||
logger.warn("[Migrator - MiniMOTD] Cannot create new file minimotd_migrated.yml. One with the same name is already present.");
|
||||
sender.sendErrorMsg(" -> <red>File</red> minimotd_migrated.yml <red>already present.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
try{
|
||||
Files.createFile(profile);
|
||||
}catch(IOException ex){
|
||||
logger.warn("[Migrator - MiniMOTD] Encountered an IOException while trying to create file minimotd_migrated.yml.", ex);
|
||||
sender.sendErrorMsg(" -> <red>File creation error.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
YamlConfigurationLoader yamlLoader = YamlConfigurationLoader.builder()
|
||||
.path(profile)
|
||||
.indent(2)
|
||||
.nodeStyle(NodeStyle.BLOCK)
|
||||
.defaultOptions(option -> option.serializers(builder -> builder.register(ProfileEntry.class, ProfileSerializer.INSTANCE)))
|
||||
.build();
|
||||
|
||||
ConfigurationNode migrated;
|
||||
try{
|
||||
migrated = yamlLoader.load();
|
||||
}catch(IOException ex){
|
||||
logger.warn("[Migrator - MiniMOTD] Encountered an IOException while trying to load file minimotd_migrated.yml.", ex);
|
||||
sender.sendErrorMsg(" -> <red>File loading error.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if(migrated == null){
|
||||
logger.warn("[Migrator - MiniMOTD] Cannot migrate Configuration. ConfigurationNode was null.");
|
||||
sender.sendErrorMsg(" -> <red>File loading error.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
try{
|
||||
migrated.node("priority").set(0);
|
||||
|
||||
if(!profiles.isEmpty()){
|
||||
migrated.node("profiles").setList(ProfileEntry.class, profiles);
|
||||
}
|
||||
|
||||
migrated.set(entry);
|
||||
}catch(SerializationException ex){
|
||||
logger.warn("[Migrator - MiniMOTD] Encountered a SerializationException while setting values.", ex);
|
||||
sender.sendErrorMsg(" -> <red>Error while updating file</red> minimotd_migrated.yml<red>.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
try{
|
||||
yamlLoader.save(migrated);
|
||||
sender.sendPrefixedMsg(" -> <green>Completed!");
|
||||
|
||||
return true;
|
||||
}catch(IOException ex){
|
||||
logger.warn("[Migrator - MiniMOTD] Encountered an IOException while trying to save new file minimotd_migrated.yml.", ex);
|
||||
sender.sendErrorMsg(" -> <red>File saving error.");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyMotd(ProfileEntry.Builder builder, MiniMOTDConfig.Motd motd){
|
||||
if(motd.line1().isEmpty() && !motd.line2().isEmpty()){
|
||||
builder.motd(List.of("<grey>", motd.line2()));
|
||||
}else
|
||||
if(!motd.line1().isEmpty() && motd.line2().isEmpty()){
|
||||
builder.motd(Collections.singletonList(motd.line1()));
|
||||
}else
|
||||
if(!motd.line1().isEmpty()){ // Line2 is by logic not empty here. So this check is not required.
|
||||
builder.motd(List.of(motd.line1(), motd.line2()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.migration.minimotd.serializing;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record MiniMOTDConfig(boolean iconEnabled, boolean motdEnabled, List<Motd> motds, PlayerCount playerCount){
|
||||
|
||||
public record Motd(String icon, String line1, String line2){}
|
||||
|
||||
public record PlayerCount(boolean hidePlayers, boolean xMoreEnabled, int xMore, boolean maxPlayersEnabled, int maxPlayers){}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.migration.minimotd.serializing;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
import org.spongepowered.configurate.serialize.TypeSerializer;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class MiniMOTDSerializer implements TypeSerializer<MiniMOTDConfig>{
|
||||
|
||||
public static final MiniMOTDSerializer MINI_MOTD_SERIALIZER = new MiniMOTDSerializer();
|
||||
public static final MotdSerializer MOTD_SERIALIZER = new MotdSerializer();
|
||||
public static final PlayerCountSerializer PLAYER_COUNT_SERIALIZER = new PlayerCountSerializer();
|
||||
|
||||
@Override
|
||||
public MiniMOTDConfig deserialize(Type type, ConfigurationNode node) throws SerializationException{
|
||||
boolean iconsEnabled = node.node("icon-enabled").getBoolean();
|
||||
boolean motdEnabled = node.node("motd-enabled").getBoolean();
|
||||
|
||||
List<MiniMOTDConfig.Motd> motds = node.node("motds").getList(MiniMOTDConfig.Motd.class);
|
||||
MiniMOTDConfig.PlayerCount playerCount = node.node("player-count-settings").get(MiniMOTDConfig.PlayerCount.class);
|
||||
|
||||
return new MiniMOTDConfig(iconsEnabled, motdEnabled, motds, playerCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Type type, @Nullable MiniMOTDConfig obj, ConfigurationNode node) throws SerializationException{
|
||||
if(obj == null){
|
||||
node.raw(null);
|
||||
return;
|
||||
}
|
||||
|
||||
node.node("icon-enabled").set(obj.iconEnabled());
|
||||
node.node("motd-enable").set(obj.motdEnabled());
|
||||
node.node("motds").setList(MiniMOTDConfig.Motd.class, obj.motds());
|
||||
node.node("player-count-settings").set(MiniMOTDConfig.PlayerCount.class, obj.playerCount());
|
||||
}
|
||||
|
||||
public static class MotdSerializer implements TypeSerializer<MiniMOTDConfig.Motd>{
|
||||
|
||||
@Override
|
||||
public MiniMOTDConfig.Motd deserialize(Type type, ConfigurationNode node) throws SerializationException{
|
||||
String icon = node.node("icon").getString("");
|
||||
String line1 = node.node("line1").getString("");
|
||||
String line2 = node.node("line2").getString("");
|
||||
|
||||
return new MiniMOTDConfig.Motd(icon, line1, line2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Type type, MiniMOTDConfig.@Nullable Motd obj, ConfigurationNode node) throws SerializationException{
|
||||
if(obj == null){
|
||||
node.raw(null);
|
||||
return;
|
||||
}
|
||||
|
||||
node.node("icon").set(obj.icon());
|
||||
node.node("line1").set(obj.line1());
|
||||
node.node("line2").set(obj.line2());
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlayerCountSerializer implements TypeSerializer<MiniMOTDConfig.PlayerCount>{
|
||||
|
||||
@Override
|
||||
public MiniMOTDConfig.PlayerCount deserialize(Type type, ConfigurationNode node) throws SerializationException{
|
||||
boolean hidePlayerCount = node.node("hide-player-count").getBoolean();
|
||||
boolean xMoreEnabled = node.node("just-x-more-settings", "just-x-more-enabled").getBoolean();
|
||||
int xMore = node.node("just-x-more-settings", "x-value").getInt();
|
||||
boolean maxPlayersEnabled = node.node("max-players-enabled").getBoolean();
|
||||
int maxPlayers = node.node("max-players").getInt();
|
||||
|
||||
|
||||
return new MiniMOTDConfig.PlayerCount(hidePlayerCount, xMoreEnabled, xMore, maxPlayersEnabled, maxPlayers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Type type, MiniMOTDConfig.@Nullable PlayerCount obj, ConfigurationNode node) throws SerializationException{
|
||||
if(obj == null){
|
||||
node.raw(null);
|
||||
return;
|
||||
}
|
||||
|
||||
node.node("allow-exceeding-maximum").set(false);
|
||||
node.node("disable-player-list-hover").set(false);
|
||||
|
||||
node.node("fake-players", "fake-players").set("25%");
|
||||
node.node("fake-players", "fake-players-enabled").set(false);
|
||||
|
||||
node.node("hide-player-count").set(obj.hidePlayers());
|
||||
|
||||
node.node("just-x-more-settings", "just-x-more-enabled").set(obj.xMoreEnabled());
|
||||
node.node("just-x-more-settings", "x-value").set(obj.xMore());
|
||||
|
||||
node.node("max-players").set(obj.maxPlayers());
|
||||
node.node("max-players-enabled").set(obj.maxPlayersEnabled());
|
||||
|
||||
node.node("servers").setList(String.class, Collections.emptyList());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.migration.serverlistplus;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.NullBool;
|
||||
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.profiles.profile.ProfileSerializer;
|
||||
import net.minecrell.serverlistplus.core.ServerListPlusCore;
|
||||
import net.minecrell.serverlistplus.core.config.PersonalizedStatusConf;
|
||||
import net.minecrell.serverlistplus.core.config.ServerStatusConf;
|
||||
import net.minecrell.serverlistplus.core.util.BooleanOrList;
|
||||
import net.minecrell.serverlistplus.core.util.IntegerRange;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
import org.spongepowered.configurate.yaml.NodeStyle;
|
||||
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class SLPConfigMigrator{
|
||||
|
||||
private static final Pattern RGB_COLOR_PATTERN = Pattern.compile("&(?<color>#[a-fA-F0-9]{6})");
|
||||
private static final Pattern COLOR_CODE_PATTERN = Pattern.compile("&(?<color>[abcdefklmnorABCDEFKLMNOR0123456789])");
|
||||
private static final Pattern RGB_GRADIENT_PATTERN = Pattern.compile(
|
||||
"%gradient" +
|
||||
"(?<start>#[a-fA-F0-9]{6})" + // Starting color
|
||||
"(?<end>#[a-fA-F0-9]{6})" + // Ending color
|
||||
"%" +
|
||||
"(?<text>.+?)" + // Text
|
||||
"%gradient%");
|
||||
|
||||
private static final Map<String, String> STATIC_REPLACEMENTS = Map.of(
|
||||
"%player%", "${player name}",
|
||||
"%uuid%", "${player uuid}",
|
||||
"%_uuid_%", "${player uuid}",
|
||||
"%online%", "${server playersOnline}",
|
||||
"%max%", "${server playersMax}");
|
||||
|
||||
public static int migrate(AdvancedServerList<?> core, CmdSender sender){
|
||||
ServerListPlusCore slpCore = ServerListPlusCore.getInstance();
|
||||
PluginLogger logger = core.getPlugin().getPluginLogger();
|
||||
if(slpCore == null){
|
||||
logger.warn("[Migrator - ServerListPlus] Cannot migrate ServerListPlus config. ServerListPlus is not active!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ServerStatusConf conf = slpCore.getConf(ServerStatusConf.class);
|
||||
if(conf == null){
|
||||
logger.warn("[Migrator - ServerListPlus] Cannot migrate ServerListPlus configuration. Received configuration was null.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
PersonalizedStatusConf.StatusConf defConf = conf.Default;
|
||||
PersonalizedStatusConf.StatusConf personalizedConf = conf.Personalized;
|
||||
PersonalizedStatusConf.StatusConf banned = conf.Banned;
|
||||
|
||||
sender.sendPrefixedMsg("Migrating <white>Default</white>...");
|
||||
int defConfParsed = parseConf(core, sender, defConf, Type.DEFAULT);
|
||||
|
||||
sender.sendPrefixedMsg("Migrating <white>Personalized</white>...");
|
||||
int personalizedParsed = parseConf(core, sender, personalizedConf, Type.PERSONALIZED);
|
||||
|
||||
sender.sendPrefixedMsg("Migrating <white>Banned</white>...");
|
||||
int bannedParsed = parseConf(core, sender, banned, Type.BANNED);
|
||||
|
||||
return defConfParsed + personalizedParsed + bannedParsed;
|
||||
}
|
||||
|
||||
private static int parseConf(AdvancedServerList<?> core, CmdSender sender, PersonalizedStatusConf.StatusConf conf, Type type){
|
||||
PluginLogger logger = core.getPlugin().getPluginLogger();
|
||||
if(conf == null){
|
||||
logger.info("[Migrator - ServerListPlus] No StatusConf found for type %s. Skipping...", type.getName());
|
||||
sender.sendPrefixedMsg(" -> Not found! Skipping...");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ProfileEntry.Builder builder = ProfileEntry.empty().builder();
|
||||
List<ProfileEntry.Builder> profiles = new ArrayList<>();
|
||||
|
||||
List<List<String>> motds = resolveMotds(conf.Description);
|
||||
List<String> favicons = resolveFavicons(conf.Favicon);
|
||||
List<List<String>> hovers = resolveHover(conf.Players);
|
||||
NullBool hidePlayers = conf.Players != null ? NullBool.resolve(conf.Players.Hidden) : NullBool.NOT_SET;
|
||||
List<Integer> maxPlayers = resolveMaxPlayers(conf.Players);
|
||||
List<String> playerCount = resolvePlayerCounts(conf.Players);
|
||||
|
||||
int size = getMaxListSize(motds, favicons, hovers, maxPlayers, playerCount);
|
||||
// Only add profiles entries when there are more than 2 entries of an option.
|
||||
// Avoids a profiles option with a single entry that has no options whatsoever.
|
||||
if(size > 1){
|
||||
for(int i = 0; i < size; i++){
|
||||
profiles.add(ProfileEntry.empty().builder());
|
||||
}
|
||||
}
|
||||
|
||||
apply(motds, builder, profiles, ProfileEntry.Builder::motd);
|
||||
apply(favicons, builder, profiles, ProfileEntry.Builder::favicon);
|
||||
apply(hovers, builder, profiles, ProfileEntry.Builder::players);
|
||||
apply(maxPlayers, builder, profiles, (build, count) -> build.maxPlayersEnabled(NullBool.TRUE).maxPlayersCount(String.valueOf(count)));
|
||||
apply(playerCount, builder, profiles, ProfileEntry.Builder::playerCountText);
|
||||
|
||||
builder.hidePlayersEnabled(hidePlayers);
|
||||
|
||||
ProfileEntry entry = builder.build();
|
||||
List<ProfileEntry> profileEntries = profiles.stream()
|
||||
.map(ProfileEntry.Builder::build)
|
||||
.toList();
|
||||
|
||||
boolean profilesInvalid = profileEntries.isEmpty() || profileEntries.stream().anyMatch(ProfileEntry::isInvalid);
|
||||
if(entry.isInvalid() && profilesInvalid){
|
||||
logger.warn("[Migrator - ServerListPlus] Unable to parse ServerListPlus configuration of type %s. Generated ProfileEntry was invalid.", type.getName());
|
||||
sender.sendErrorMsg(" -> <red>Received invalid Configuration.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Path profile = core.getPlugin().getFolderPath().resolve("profiles").resolve(type.getFile());
|
||||
if(Files.exists(profile)){
|
||||
logger.warn("[Migrator - ServerListPlus] Cannot create new file %s. One with the same name is already present", type.getFile());
|
||||
sender.sendErrorMsg(" -> <red>File</red> %s <red>already present.", type.getFile());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
try{
|
||||
Files.createFile(profile);
|
||||
}catch(IOException ex){
|
||||
logger.warn("[Migrator - ServerListPlus] Encountered an IOException while trying to create file %s.", ex, type.getFile());
|
||||
sender.sendErrorMsg(" -> <red>File creation error.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
YamlConfigurationLoader loader = YamlConfigurationLoader.builder()
|
||||
.path(profile)
|
||||
.indent(2)
|
||||
.nodeStyle(NodeStyle.BLOCK)
|
||||
.defaultOptions(opt -> opt.serializers(build -> build.register(ProfileEntry.class, ProfileSerializer.INSTANCE)))
|
||||
.build();
|
||||
|
||||
ConfigurationNode node;
|
||||
try{
|
||||
node = loader.load();
|
||||
}catch(IOException ex){
|
||||
logger.warn("[Migrator - ServerListPlus] Encountered an IOException while trying to load file %s.", ex, type.getFile());
|
||||
sender.sendErrorMsg(" -> <red>File loading error.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(node == null){
|
||||
logger.warn("[Migrator - ServerListPlus] Cannot migrate Configuration of type %s. ConfigurationNode was null.", type.getName());
|
||||
sender.sendErrorMsg(" -> <red>File loading error.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
try{
|
||||
node.node("priority")
|
||||
.set(type != Type.DEFAULT ? 1 : 0);
|
||||
|
||||
if(type == Type.PERSONALIZED){
|
||||
node.node("condition")
|
||||
.set("${player name} != \"" +
|
||||
core.getFileHandler().getString("Anonymous", "unknownPlayer", "name") +
|
||||
"\"");
|
||||
}else
|
||||
if(type == Type.BANNED){
|
||||
node.node("condition")
|
||||
.set("${player isBanned}");
|
||||
}
|
||||
|
||||
if(!profiles.isEmpty()){
|
||||
node.node("profiles").setList(ProfileEntry.class, profileEntries);
|
||||
}
|
||||
|
||||
node.set(entry);
|
||||
}catch(SerializationException ex){
|
||||
logger.warn("[Migrator - ServerListPlus] Encountered a SerializationException while setting values.", ex);
|
||||
sender.sendErrorMsg(" -> <red>Error while updating file</red> %s<red>.", type.getFile());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
try{
|
||||
loader.save(node);
|
||||
sender.sendPrefixedMsg(" -> <green>Completed!");
|
||||
|
||||
return 1;
|
||||
}catch(IOException ex){
|
||||
logger.warn("[Migrator - ServerListPlus] Encountered an IOException while trying to save new file %s.", ex, type.getFile());
|
||||
sender.sendErrorMsg(" -> <red>File saving error.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getMaxListSize(List<?>... lists){
|
||||
int size = 0;
|
||||
for(List<?> list : lists){
|
||||
if(list.size() > size)
|
||||
size = list.size();
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
private static List<List<String>> resolveMotds(List<String> descriptions){
|
||||
if(descriptions == null || descriptions.isEmpty())
|
||||
return Collections.emptyList();
|
||||
|
||||
List<List<String>> motds = new ArrayList<>();
|
||||
for(String line : descriptions){
|
||||
motds.add(getList(line));
|
||||
}
|
||||
|
||||
return motds;
|
||||
}
|
||||
|
||||
private static List<String> resolveFavicons(PersonalizedStatusConf.StatusConf.FaviconConf faviconConf){
|
||||
if(faviconConf == null || faviconConf.Disabled != null && faviconConf.Disabled)
|
||||
return Collections.emptyList();
|
||||
|
||||
List<String> list = new ArrayList<>();
|
||||
|
||||
if(faviconConf.Heads != null){
|
||||
for(String head : faviconConf.Heads){
|
||||
list.add(
|
||||
"https://mc-heads.net/avatar/" +
|
||||
replacePlaceholders(head) +
|
||||
"/64/nohelm"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if(faviconConf.Helms != null){
|
||||
for(String helm : faviconConf.Helms){
|
||||
list.add(replacePlaceholders(helm));
|
||||
}
|
||||
}
|
||||
|
||||
if(faviconConf.Files != null){
|
||||
for(String file : faviconConf.Files){
|
||||
if(!file.toLowerCase(Locale.ROOT).endsWith(".png"))
|
||||
continue;
|
||||
|
||||
list.add(replacePlaceholders(file));
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static List<List<String>> resolveHover(PersonalizedStatusConf.StatusConf.PlayersConf playersConf){
|
||||
if(playersConf == null)
|
||||
return Collections.emptyList();
|
||||
|
||||
BooleanOrList<String> booleanOrList = playersConf.Hover;
|
||||
if(booleanOrList == null)
|
||||
return Collections.emptyList();
|
||||
|
||||
if(booleanOrList.getList() != null){
|
||||
List<List<String>> hovers = new ArrayList<>();
|
||||
for(String line : booleanOrList.getList()){
|
||||
if(line == null)
|
||||
continue;
|
||||
|
||||
hovers.add(getList(line));
|
||||
}
|
||||
|
||||
return hovers;
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static List<Integer> resolveMaxPlayers(PersonalizedStatusConf.StatusConf.PlayersConf playersConf){
|
||||
if(playersConf == null)
|
||||
return Collections.emptyList();
|
||||
|
||||
List<IntegerRange> maxPlayers = playersConf.Max;
|
||||
if(maxPlayers == null)
|
||||
return Collections.emptyList();
|
||||
|
||||
List<Integer> values = new ArrayList<>();
|
||||
for(IntegerRange range : maxPlayers){
|
||||
if(range == null)
|
||||
continue;
|
||||
|
||||
values.add(range.to());
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
private static List<String> resolvePlayerCounts(PersonalizedStatusConf.StatusConf.PlayersConf playersConf){
|
||||
if(playersConf == null)
|
||||
return Collections.emptyList();
|
||||
|
||||
if(playersConf.Slots == null)
|
||||
return Collections.emptyList();
|
||||
|
||||
return playersConf.Slots.stream()
|
||||
.map(SLPConfigMigrator::replacePlaceholders)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private static <T> void apply(List<T> list, ProfileEntry.Builder builder, List<ProfileEntry.Builder> profiles,
|
||||
BiConsumer<ProfileEntry.Builder, T> builderConsumer){
|
||||
if(list.size() == 1){
|
||||
builderConsumer.accept(builder, list.get(0));
|
||||
}else
|
||||
if(list.size() > 1){
|
||||
for(int i = 0; i < list.size(); i++){
|
||||
builderConsumer.accept(profiles.get(i), list.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> getList(String line){
|
||||
return Arrays.asList(replacePlaceholders(line).split("\n"));
|
||||
}
|
||||
|
||||
private static String replacePlaceholders(String input){
|
||||
StringBuilder parsed = new StringBuilder(input);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
Matcher rgbColorMatcher = RGB_COLOR_PATTERN.matcher(parsed.toString());
|
||||
if(rgbColorMatcher.find()){
|
||||
do{
|
||||
rgbColorMatcher.appendReplacement(builder, "<" + rgbColorMatcher.group("color") + ">");
|
||||
}while(rgbColorMatcher.find());
|
||||
|
||||
rgbColorMatcher.appendTail(builder);
|
||||
parsed = builder;
|
||||
}
|
||||
|
||||
Matcher colorCodeMatcher = COLOR_CODE_PATTERN.matcher(parsed.toString());
|
||||
if(colorCodeMatcher.find()){
|
||||
builder.setLength(0);
|
||||
do{
|
||||
String color = colorCodeMatcher.group("color").toLowerCase(Locale.ROOT);
|
||||
switch(color){
|
||||
case "a" -> colorCodeMatcher.appendReplacement(builder, "<green>");
|
||||
case "b" -> colorCodeMatcher.appendReplacement(builder, "<aqua>");
|
||||
case "c" -> colorCodeMatcher.appendReplacement(builder, "<red>");
|
||||
case "d" -> colorCodeMatcher.appendReplacement(builder, "<light_purple>");
|
||||
case "e" -> colorCodeMatcher.appendReplacement(builder, "<yellow>");
|
||||
case "f" -> colorCodeMatcher.appendReplacement(builder, "<white>");
|
||||
case "k" -> colorCodeMatcher.appendReplacement(builder, "<obfuscated>");
|
||||
case "l" -> colorCodeMatcher.appendReplacement(builder, "<bold>");
|
||||
case "m" -> colorCodeMatcher.appendReplacement(builder, "<strikethrough>");
|
||||
case "n" -> colorCodeMatcher.appendReplacement(builder, "<underlined>");
|
||||
case "o" -> colorCodeMatcher.appendReplacement(builder, "<italic>");
|
||||
case "r" -> colorCodeMatcher.appendReplacement(builder, "<reset>");
|
||||
case "0" -> colorCodeMatcher.appendReplacement(builder, "<black>");
|
||||
case "1" -> colorCodeMatcher.appendReplacement(builder, "<dark_blue>");
|
||||
case "2" -> colorCodeMatcher.appendReplacement(builder, "<dark_green>");
|
||||
case "3" -> colorCodeMatcher.appendReplacement(builder, "<dark_aqua>");
|
||||
case "4" -> colorCodeMatcher.appendReplacement(builder, "<dark_red>");
|
||||
case "5" -> colorCodeMatcher.appendReplacement(builder, "<dark_purple>");
|
||||
case "6" -> colorCodeMatcher.appendReplacement(builder, "<gold>");
|
||||
case "7" -> colorCodeMatcher.appendReplacement(builder, "<grey>");
|
||||
case "8" -> colorCodeMatcher.appendReplacement(builder, "<dark_grey>");
|
||||
case "9" -> colorCodeMatcher.appendReplacement(builder, "<blue>");
|
||||
// This one shouldn't be reachable given the pattern, but you never know...
|
||||
default -> colorCodeMatcher.appendReplacement(builder, "&" + color);
|
||||
}
|
||||
}while(colorCodeMatcher.find());
|
||||
|
||||
colorCodeMatcher.appendTail(builder);
|
||||
parsed = builder;
|
||||
}
|
||||
|
||||
Matcher rgbGradientMatcher = RGB_GRADIENT_PATTERN.matcher(parsed.toString());
|
||||
if(rgbGradientMatcher.find()){
|
||||
builder.setLength(0);
|
||||
do{
|
||||
String start = rgbGradientMatcher.group("start");
|
||||
String end = rgbGradientMatcher.group("end");
|
||||
String text = rgbGradientMatcher.group("text");
|
||||
|
||||
rgbGradientMatcher.appendReplacement(builder, "<gradient:" + start + ":" + end + ">" + text + "</gradient>");
|
||||
}while(rgbGradientMatcher.find());
|
||||
|
||||
rgbGradientMatcher.appendTail(builder);
|
||||
parsed = builder;
|
||||
}
|
||||
|
||||
return replaceStaticPlaceholders(parsed);
|
||||
}
|
||||
|
||||
private static String replaceStaticPlaceholders(StringBuilder input){
|
||||
if(input.isEmpty())
|
||||
return "";
|
||||
|
||||
for(Map.Entry<String, String> entry : STATIC_REPLACEMENTS.entrySet()){
|
||||
int index = 0;
|
||||
while((index = input.indexOf(entry.getKey(), index)) != -1){
|
||||
input.replace(index, index + entry.getKey().length(), entry.getValue());
|
||||
index += entry.getValue().length();
|
||||
}
|
||||
}
|
||||
|
||||
return input.toString();
|
||||
}
|
||||
|
||||
private enum Type{
|
||||
DEFAULT("Default", "slp_default.yml"),
|
||||
PERSONALIZED("Personalized", "slp_personalized.yml"),
|
||||
BANNED("Banned", "slp_banned.yml");
|
||||
|
||||
private final String name, file;
|
||||
|
||||
Type(String name, String file){
|
||||
this.name = name;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public String getName(){
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getFile(){
|
||||
return file;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.objects;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class CacheUtil<T>{
|
||||
|
||||
private final Duration duration;
|
||||
private CacheValue<T> cache = null;
|
||||
|
||||
public CacheUtil(Duration duration){
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
public T get(Supplier<T> supplier){
|
||||
if(cache == null || cache.expired())
|
||||
cache = new CacheValue<>(System.currentTimeMillis(), duration, supplier.get());
|
||||
|
||||
return cache.value();
|
||||
}
|
||||
|
||||
private record CacheValue<T>(long timestamp, Duration duration, T value){
|
||||
public boolean expired(){
|
||||
return (timestamp < 0L) || System.currentTimeMillis() - timestamp >= duration.toMillis();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,28 +27,4 @@ package ch.andre601.advancedserverlist.core.objects;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@SuppressWarnings("FieldMayBeFinal")
|
||||
public class CachedPlayer{
|
||||
|
||||
private String ip;
|
||||
private String name;
|
||||
private UUID uuid;
|
||||
|
||||
public CachedPlayer(String ip, String name, UUID uuid){
|
||||
this.ip = ip;
|
||||
this.name = name;
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public String getIp(){
|
||||
return ip;
|
||||
}
|
||||
|
||||
public String getName(){
|
||||
return name;
|
||||
}
|
||||
|
||||
public UUID getUuid(){
|
||||
return uuid;
|
||||
}
|
||||
}
|
||||
public record CachedPlayer(String ip, String name, UUID uuid){}
|
||||
@@ -65,6 +65,10 @@ public class ComponentParser{
|
||||
return mm.deserialize(text);
|
||||
}
|
||||
|
||||
public static String toMMString(Component component){
|
||||
return mm.serialize(component);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return legacy.serialize(toComponent());
|
||||
|
||||
@@ -28,8 +28,9 @@ package ch.andre601.advancedserverlist.core.profiles;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.Expression;
|
||||
import ch.andre601.expressionparser.ParseWarnCollector;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
@@ -37,38 +38,30 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class ServerListProfile{
|
||||
public record ServerListProfile(int priority, String condition, ProfileEntry defaultProfile, List<ProfileEntry> profiles, String file){
|
||||
private static final Random random = new Random();
|
||||
|
||||
private final int priority;
|
||||
private final List<Expression> expressions;
|
||||
private final ProfileEntry defaultProfile;
|
||||
|
||||
private final List<ProfileEntry> profiles;
|
||||
|
||||
private final Random random = new Random();
|
||||
|
||||
public ServerListProfile(int priority, List<Expression> expressions, ProfileEntry defaultProfile, List<ProfileEntry> profiles){
|
||||
this.priority = priority;
|
||||
this.expressions = expressions;
|
||||
this.defaultProfile = defaultProfile;
|
||||
|
||||
this.profiles = profiles;
|
||||
}
|
||||
|
||||
public int getPriority(){
|
||||
return priority;
|
||||
}
|
||||
|
||||
public boolean evalConditions(GenericPlayer player, GenericServer server){
|
||||
if(expressions.isEmpty())
|
||||
public boolean evalConditions(AdvancedServerList<?> core, GenericPlayer player, GenericServer server){
|
||||
if(condition == null || condition.isEmpty())
|
||||
return true;
|
||||
|
||||
for(Expression expression : expressions){
|
||||
if(!expression.evaluate(player, server))
|
||||
return false;
|
||||
ParseWarnCollector collector = new ParseWarnCollector(condition);
|
||||
PluginLogger logger = core.getPlugin().getPluginLogger();
|
||||
|
||||
boolean result = core.getParser().evaluate(condition, player, server, collector);
|
||||
|
||||
if(collector.hasWarnings()){
|
||||
logger.warn("Encountered %d Error(s) while parsing condition for file '%s':", collector.getWarnings().size(), file);
|
||||
|
||||
for(ParseWarnCollector.Context context : collector.getWarnings()){
|
||||
logger.warn(" - At position %s:", context.position() == -1 ? "Unknown" : context.position());
|
||||
logger.warn(" %s", condition);
|
||||
logger.warn(" ".repeat(context.position() + 4) + "^");
|
||||
logger.warn(" -> %s", context.message());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
||||
public ProfileEntry getRandomProfile(){
|
||||
@@ -84,10 +77,6 @@ public class ServerListProfile{
|
||||
}
|
||||
}
|
||||
|
||||
public ProfileEntry getDefaultProfile(){
|
||||
return defaultProfile;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the Profile...
|
||||
* ...doesn't have any valid MOTD set AND
|
||||
@@ -96,12 +85,12 @@ public class ServerListProfile{
|
||||
* ...doesn't have a favicon set.
|
||||
*/
|
||||
public boolean isInvalidProfile(){
|
||||
if(profiles.isEmpty())
|
||||
return defaultProfile.isInvalid();
|
||||
if(profiles().isEmpty())
|
||||
return defaultProfile().isInvalid();
|
||||
|
||||
boolean profilesValid = false;
|
||||
|
||||
for(ProfileEntry profile : profiles){
|
||||
for(ProfileEntry profile : profiles()){
|
||||
if(profile.isInvalid())
|
||||
continue;
|
||||
|
||||
@@ -117,10 +106,10 @@ public class ServerListProfile{
|
||||
private final String fileName;
|
||||
private final ConfigurationNode node;
|
||||
private final int priority;
|
||||
private final List<Expression> expressions = new ArrayList<>();
|
||||
|
||||
private final PluginLogger logger;
|
||||
|
||||
private String condition = null;
|
||||
private List<ProfileEntry> profiles = new ArrayList<>();
|
||||
private ProfileEntry defaultProfile = ProfileEntry.empty();
|
||||
|
||||
@@ -133,38 +122,27 @@ public class ServerListProfile{
|
||||
|
||||
public static Builder resolve(String fileName, ConfigurationNode node, PluginLogger logger){
|
||||
return new Builder(fileName, node, logger)
|
||||
.resolveExpressions()
|
||||
.resolveCondition()
|
||||
.resolveProfiles()
|
||||
.resolveDefaultProfile();
|
||||
}
|
||||
|
||||
private Builder resolveExpressions(){
|
||||
List<String> temp;
|
||||
try{
|
||||
temp = node.node("conditions").getList(String.class);
|
||||
}catch(SerializationException ex){
|
||||
logger.warn("Encountered a SerializationException while resolving conditions for %s", ex, fileName);
|
||||
return this;
|
||||
}
|
||||
private Builder resolveCondition(){
|
||||
if(node == null)
|
||||
throw new IllegalStateException("ConfigurationNode was null!");
|
||||
|
||||
if(temp == null || temp.isEmpty())
|
||||
String condition = node.node("condition").getString();
|
||||
if(condition == null || condition.isEmpty())
|
||||
return this;
|
||||
|
||||
for(String str : temp){
|
||||
Expression expr = Expression.resolve(str);
|
||||
|
||||
if(expr.getResult() != Expression.ExpressionResult.VALID){
|
||||
logger.warn("Found invalid expression in condition '%s'! Reason: %s", str, expr.getResult().getMessage());
|
||||
continue;
|
||||
}
|
||||
|
||||
this.expressions.add(expr);
|
||||
}
|
||||
|
||||
this.condition = condition;
|
||||
return this;
|
||||
}
|
||||
|
||||
private Builder resolveProfiles(){
|
||||
if(node == null)
|
||||
throw new IllegalStateException("ConfigurationNode was null!");
|
||||
|
||||
try{
|
||||
this.profiles = node.node("profiles").getList(ProfileEntry.class);
|
||||
}catch(SerializationException ex){
|
||||
@@ -175,6 +153,9 @@ public class ServerListProfile{
|
||||
}
|
||||
|
||||
private Builder resolveDefaultProfile(){
|
||||
if(node == null)
|
||||
throw new IllegalStateException("ConfigurationNode was null!");
|
||||
|
||||
try{
|
||||
this.defaultProfile = node.get(ProfileEntry.class);
|
||||
}catch(SerializationException ex){
|
||||
@@ -184,7 +165,7 @@ public class ServerListProfile{
|
||||
}
|
||||
|
||||
public ServerListProfile build(){
|
||||
return new ServerListProfile(this.priority, this.expressions, this.defaultProfile, this.profiles);
|
||||
return new ServerListProfile(this.priority, this.condition, this.defaultProfile, this.profiles, this.fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.profiles.conditions;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.core.profiles.replacer.StringReplacer;
|
||||
|
||||
public class Expression{
|
||||
|
||||
private final ExpressionResult result;
|
||||
|
||||
private final String left;
|
||||
private final Operator operator;
|
||||
private final String right;
|
||||
|
||||
private Expression(ExpressionResult result, String left, Operator operator, String right){
|
||||
this.result = result;
|
||||
this.left = left;
|
||||
this.operator = operator;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
public static Expression createInvalid(ExpressionResult result){
|
||||
return new Expression(result, null, Operator.UNKNOWN, null);
|
||||
}
|
||||
|
||||
public static Expression resolve(String expression){
|
||||
Operator operator = Operator.UNKNOWN;
|
||||
|
||||
if(expression == null || expression.isEmpty()){
|
||||
return Expression.createInvalid(ExpressionResult.INVALID_NO_EXPRESSION);
|
||||
}
|
||||
|
||||
char[] chars = expression.toCharArray();
|
||||
|
||||
StringBuilder leftBuilder = new StringBuilder();
|
||||
StringBuilder rightBuilder = new StringBuilder();
|
||||
|
||||
boolean foundOperator = false;
|
||||
|
||||
for(int i = 0; i < chars.length; i++){
|
||||
char c = chars[i];
|
||||
char next = (i == (chars.length - 1)) ? '\0' : chars[i + 1];
|
||||
|
||||
Operator tmp = Operator.getOperand(c, next);
|
||||
if(tmp != Operator.UNKNOWN && !foundOperator){
|
||||
operator = tmp;
|
||||
foundOperator = true;
|
||||
|
||||
if(tmp.hasSecond())
|
||||
i++; // Skip next character if operator has 2
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if(foundOperator){
|
||||
rightBuilder.append(c);
|
||||
}else{
|
||||
leftBuilder.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
if(leftBuilder.isEmpty() || rightBuilder.isEmpty()){
|
||||
return Expression.createInvalid(ExpressionResult.INVALID_EMPTY_PARTS);
|
||||
}
|
||||
|
||||
return new Expression(ExpressionResult.VALID, leftBuilder.toString().trim(), operator, rightBuilder.toString().trim());
|
||||
}
|
||||
|
||||
public boolean evaluate(GenericPlayer player, GenericServer server){
|
||||
String newLeft = StringReplacer.replace(left, player, server);
|
||||
String newRight = StringReplacer.replace(right, player, server);
|
||||
|
||||
return operator.evaluate(newLeft, newRight);
|
||||
}
|
||||
|
||||
public ExpressionResult getResult(){
|
||||
return result;
|
||||
}
|
||||
|
||||
public enum Operator{
|
||||
LESS('<', '\0'){
|
||||
@Override
|
||||
public boolean evaluate(String left, String right){
|
||||
int leftInt = Operator.getInt(left);
|
||||
int rightInt = Operator.getInt(right);
|
||||
|
||||
return leftInt < rightInt;
|
||||
}
|
||||
},
|
||||
LESS_OR_EQUAL('<', '='){
|
||||
@Override
|
||||
public boolean evaluate(String left, String right){
|
||||
int leftInt = Operator.getInt(left);
|
||||
int rightInt = Operator.getInt(right);
|
||||
|
||||
return leftInt <= rightInt;
|
||||
}
|
||||
},
|
||||
EQUAL('=', '\0'){
|
||||
@Override
|
||||
public boolean evaluate(String left, String right){
|
||||
return left.equals(right);
|
||||
}
|
||||
},
|
||||
EQUALS_IGNORE_CASE('~', '='){
|
||||
@Override
|
||||
public boolean evaluate(String left, String right){
|
||||
return left.equalsIgnoreCase(right);
|
||||
}
|
||||
},
|
||||
NOT_EQUAL('!', '='){
|
||||
@Override
|
||||
public boolean evaluate(String left, String right){
|
||||
return !left.equals(right);
|
||||
}
|
||||
},
|
||||
NOT_EQUAL_IGNORE_CASE('!', '~'){
|
||||
@Override
|
||||
public boolean evaluate(String left, String right){
|
||||
return !left.equalsIgnoreCase(right);
|
||||
}
|
||||
},
|
||||
GREATER('>', '\0'){
|
||||
@Override
|
||||
public boolean evaluate(String left, String right){
|
||||
int leftInt = Operator.getInt(left);
|
||||
int rightInt = Operator.getInt(right);
|
||||
|
||||
return leftInt > rightInt;
|
||||
}
|
||||
},
|
||||
GREATER_OR_EQUAL('>', '='){
|
||||
@Override
|
||||
public boolean evaluate(String left, String right){
|
||||
int leftInt = Operator.getInt(left);
|
||||
int rightInt = Operator.getInt(right);
|
||||
|
||||
return leftInt >= rightInt;
|
||||
}
|
||||
},
|
||||
|
||||
UNKNOWN('\0', '\0'){
|
||||
@Override
|
||||
public boolean evaluate(String left, String right){
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
private static final Operator[] VALUES = values();
|
||||
|
||||
private final char first;
|
||||
private final char second;
|
||||
|
||||
Operator(char first, char second){
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
public static Operator getOperand(char first, char second){
|
||||
for(Operator operator : VALUES){
|
||||
if(operator.first == first){
|
||||
if((!operator.hasSecond() && second != '=' && second != '~') || operator.second == second)
|
||||
return operator;
|
||||
}
|
||||
}
|
||||
|
||||
return Operator.UNKNOWN;
|
||||
}
|
||||
|
||||
public boolean hasSecond(){
|
||||
return second != '\0';
|
||||
}
|
||||
|
||||
public abstract boolean evaluate(String left, String right);
|
||||
|
||||
private static int getInt(String text){
|
||||
try{
|
||||
return Integer.parseInt(text);
|
||||
}catch(NumberFormatException ex){
|
||||
return text.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum ExpressionResult{
|
||||
VALID(null),
|
||||
|
||||
INVALID_NO_EXPRESSION("Received empty condition which isn't allowed!"),
|
||||
INVALID_EMPTY_PARTS ("Received condition with either left or right part being empty.");
|
||||
|
||||
private final String message;
|
||||
|
||||
ExpressionResult(String message){
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage(){
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.profiles.conditions;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.reader.PlaceholderTokenReader;
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.reader.PlaceholderValueReader;
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.tokens.CustomExpressionTokenizer;
|
||||
import ch.andre601.expressionparser.ExpressionParserEngine;
|
||||
import ch.andre601.expressionparser.ParseWarnCollector;
|
||||
import ch.andre601.expressionparser.operator.Operator;
|
||||
import ch.andre601.expressionparser.parsers.ExpressionTemplateParser;
|
||||
import ch.andre601.expressionparser.parsers.ValueReader;
|
||||
import ch.andre601.expressionparser.templates.ExpressionTemplate;
|
||||
import ch.andre601.expressionparser.tokens.Token;
|
||||
import ch.andre601.expressionparser.tokens.readers.TokenReader;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ProfileConditionParser implements ExpressionParserEngine{
|
||||
|
||||
private final CustomExpressionTokenizer tokenizer;
|
||||
private final ExpressionTemplateParser parser;
|
||||
|
||||
private ProfileConditionParser(List<TokenReader> tokenReaders, ImmutableMap<Token, Operator> operators, ImmutableList<ValueReader> valueReaders){
|
||||
this.tokenizer = new CustomExpressionTokenizer(tokenReaders);
|
||||
this.parser = new ExpressionTemplateParser(operators, valueReaders);
|
||||
}
|
||||
|
||||
public static ProfileConditionParser create(){
|
||||
return new ProfileConditionParserBuilder().addDefaults()
|
||||
.addTokenReader(new PlaceholderTokenReader(-20))
|
||||
.addValueReader(new PlaceholderValueReader())
|
||||
.build();
|
||||
}
|
||||
|
||||
public boolean evaluate(String text, GenericPlayer player, GenericServer server, ParseWarnCollector collector){
|
||||
ExpressionTemplate result = parser.parse(tokenizer.parse(text, player, server, collector), collector);
|
||||
if(result == null)
|
||||
return false;
|
||||
|
||||
return result.returnBooleanExpression().evaluate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionTemplate compile(String s, ParseWarnCollector parseWarnCollector){
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class ProfileConditionParserBuilder extends ExpressionParserEngine.Builder<ProfileConditionParser>{
|
||||
@Override
|
||||
public ProfileConditionParser build(){
|
||||
return new ProfileConditionParser(this.getTokenReaders(), ImmutableMap.copyOf(this.getOperators()), ImmutableList.copyOf(this.getValueReaders()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.reader;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.expressionparser.ParseWarnCollector;
|
||||
import ch.andre601.expressionparser.tokens.Token;
|
||||
import ch.andre601.expressionparser.tokens.readers.TokenReader;
|
||||
|
||||
import java.text.ParsePosition;
|
||||
|
||||
public abstract class CustomTokenReader extends TokenReader{
|
||||
|
||||
public CustomTokenReader(int priority){
|
||||
super(priority);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token read(String text, ParsePosition position, ParseWarnCollector collector){
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract Token read(String text, ParsePosition position, GenericPlayer player, GenericServer server, ParseWarnCollector collector);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.reader;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.tokens.PlaceholderToken;
|
||||
import ch.andre601.advancedserverlist.core.profiles.replacer.StringReplacer;
|
||||
import ch.andre601.expressionparser.ParseWarnCollector;
|
||||
import ch.andre601.expressionparser.tokens.Token;
|
||||
|
||||
import java.text.ParsePosition;
|
||||
|
||||
public class PlaceholderTokenReader extends CustomTokenReader{
|
||||
|
||||
public PlaceholderTokenReader(int priority){
|
||||
super(priority);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token read(String text, ParsePosition position, GenericPlayer player, GenericServer server, ParseWarnCollector collector){
|
||||
if(position.getIndex() + 1 < text.length() && text.charAt(position.getIndex()) == '$' && text.charAt(position.getIndex() + 1) == '{'){
|
||||
position.setIndex(position.getIndex() + 2);
|
||||
return new PlaceholderToken(StringReplacer.parsePlaceholder(text, position, player, server, collector));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.reader;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.tokens.PlaceholderToken;
|
||||
import ch.andre601.expressionparser.ParseWarnCollector;
|
||||
import ch.andre601.expressionparser.parsers.ExpressionTemplateParser;
|
||||
import ch.andre601.expressionparser.parsers.ValueReader;
|
||||
import ch.andre601.expressionparser.templates.ExpressionTemplate;
|
||||
import ch.andre601.expressionparser.tokens.Token;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PlaceholderValueReader extends ValueReader{
|
||||
@Override
|
||||
public ExpressionTemplate read(ExpressionTemplateParser parser, List<Token> tokens, ParseWarnCollector collector){
|
||||
if(tokens.get(0) instanceof PlaceholderToken){
|
||||
PlaceholderToken token = (PlaceholderToken)tokens.remove(0);
|
||||
return token.getValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.tokens;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.reader.CustomTokenReader;
|
||||
import ch.andre601.expressionparser.ParseWarnCollector;
|
||||
import ch.andre601.expressionparser.tokens.Token;
|
||||
import ch.andre601.expressionparser.tokens.readers.TokenReader;
|
||||
import com.google.common.collect.Ordering;
|
||||
|
||||
import java.text.ParsePosition;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class CustomExpressionTokenizer{
|
||||
|
||||
private static final Ordering<TokenReader> TOKEN_READER_ORDERING = Ordering.from(Comparator.comparingInt(TokenReader::getPriority)).reverse();
|
||||
|
||||
private final List<TokenReader> tokenReaders;
|
||||
|
||||
public CustomExpressionTokenizer(Iterable<TokenReader> tokenReaders){
|
||||
this.tokenReaders = TOKEN_READER_ORDERING.immutableSortedCopy(tokenReaders);
|
||||
}
|
||||
|
||||
public List<Token> parse(String text, GenericPlayer player, GenericServer server, ParseWarnCollector collector){
|
||||
ParsePosition position = new ParsePosition(0);
|
||||
|
||||
List<Token> tokens = new LinkedList<>();
|
||||
|
||||
next_token:
|
||||
while(true){
|
||||
while(position.getIndex() < text.length() && Character.isWhitespace(text.charAt(position.getIndex())))
|
||||
position.setIndex(position.getIndex() + 1);
|
||||
|
||||
if(position.getIndex() >= text.length())
|
||||
break;
|
||||
|
||||
for(TokenReader tokenReader : tokenReaders){
|
||||
Token token;
|
||||
if(tokenReader instanceof CustomTokenReader customTokenReader){
|
||||
if(null != (token = customTokenReader.read(text, position, player, server, collector))){
|
||||
tokens.add(token);
|
||||
continue next_token;
|
||||
}
|
||||
}else{
|
||||
if(null != (token = tokenReader.read(text, position, collector))){
|
||||
tokens.add(token);
|
||||
continue next_token;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collector.appendWarningFormatted(position.getIndex(), "Illegal Token '%c'.", text.charAt(position.getIndex()));
|
||||
break;
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.tokens;
|
||||
|
||||
import ch.andre601.expressionparser.expressions.ToBooleanExpression;
|
||||
import ch.andre601.expressionparser.expressions.ToDoubleExpression;
|
||||
import ch.andre601.expressionparser.expressions.ToStringExpression;
|
||||
import ch.andre601.expressionparser.templates.ExpressionTemplate;
|
||||
import ch.andre601.expressionparser.tokens.Token;
|
||||
|
||||
public class PlaceholderToken extends Token{
|
||||
|
||||
private final Placeholder value;
|
||||
|
||||
public PlaceholderToken(Placeholder value){
|
||||
super("PLACEHOLDER");
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Placeholder getValue(){
|
||||
return value;
|
||||
}
|
||||
|
||||
public static class Placeholder implements ExpressionTemplate{
|
||||
|
||||
private final ToBooleanExpression toBooleanExpression;
|
||||
private final ToDoubleExpression toDoubleExpression;
|
||||
private final ToStringExpression toStringExpression;
|
||||
|
||||
public Placeholder(String value){
|
||||
double doubleValue;
|
||||
try{
|
||||
doubleValue = Double.parseDouble(value);
|
||||
}catch(NumberFormatException ex){
|
||||
doubleValue = value.length();
|
||||
}
|
||||
|
||||
this.toBooleanExpression = ToBooleanExpression.literal(Boolean.parseBoolean(value));
|
||||
this.toDoubleExpression = ToDoubleExpression.literal(doubleValue);
|
||||
this.toStringExpression = ToStringExpression.literal(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ToBooleanExpression returnBooleanExpression(){
|
||||
return toBooleanExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ToDoubleExpression returnDoubleExpression(){
|
||||
return toDoubleExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ToStringExpression returnStringExpression(){
|
||||
return toStringExpression;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.profiles.favicon;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class FaviconHandler<F>{
|
||||
|
||||
private final Cache<String, F> favicons = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(5, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
private final AdvancedServerList core;
|
||||
|
||||
public FaviconHandler(AdvancedServerList core){
|
||||
this.core = core;
|
||||
}
|
||||
|
||||
public F getFavicon(String input, Function<BufferedImage, F> function){
|
||||
try{
|
||||
return favicons.get(input, () -> {
|
||||
BufferedImage image = resolveImage(core, input);
|
||||
if(image == null)
|
||||
return null;
|
||||
|
||||
return function.apply(image);
|
||||
});
|
||||
}catch(ExecutionException ignored){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void clearCache(){
|
||||
favicons.invalidateAll();
|
||||
}
|
||||
|
||||
private BufferedImage resolveImage(AdvancedServerList core, String input){
|
||||
InputStream stream;
|
||||
|
||||
if(input.toLowerCase(Locale.ROOT).startsWith("https://")){
|
||||
stream = getFromUrl(core, input);
|
||||
}else
|
||||
if(input.toLowerCase(Locale.ROOT).endsWith(".png")){
|
||||
File folder = core.getPlugin().getFolderPath().resolve("favicons").toFile();
|
||||
if(!folder.exists()){
|
||||
core.getPlugin().getPluginLogger().warn("Cannot get Favicon %s from favicons folder. Folder doesn't exist!", input);
|
||||
return null;
|
||||
}
|
||||
|
||||
File file = new File(folder, input);
|
||||
|
||||
try{
|
||||
stream = new FileInputStream(file);
|
||||
}catch(IOException ex){
|
||||
core.getPlugin().getPluginLogger().warn("Cannot create Favicon from File %s.", input);
|
||||
core.getPlugin().getPluginLogger().warn("Cause: %s", ex.getMessage());
|
||||
return null;
|
||||
}
|
||||
}else{
|
||||
stream = getFromUrl(core, "https://mc-heads.net/avatar/" + input + "/64");
|
||||
}
|
||||
|
||||
if(stream == null){
|
||||
core.getPlugin().getPluginLogger().warn("Cannot create Favicon. InputStream was null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
try{
|
||||
BufferedImage original = ImageIO.read(stream);
|
||||
if(original == null){
|
||||
core.getPlugin().getPluginLogger().warn("Cannot create Favicon. Unable to create BufferedImage.");
|
||||
return null;
|
||||
}
|
||||
|
||||
BufferedImage favicon = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D img = favicon.createGraphics();
|
||||
|
||||
// Resize original image to 64x64 and apply to favicon.
|
||||
img.drawImage(original, 0, 0, 64, 64, null);
|
||||
img.dispose();
|
||||
|
||||
return favicon;
|
||||
}catch(IOException ex){
|
||||
core.getPlugin().getPluginLogger().warn("Unable to create Favicon. Encountered IOException during creation.");
|
||||
core.getPlugin().getPluginLogger().warn("Cause: %s", ex.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream getFromUrl(AdvancedServerList core, String url){
|
||||
try{
|
||||
URL faviconUrl = new URL(url);
|
||||
URLConnection connection = faviconUrl.openConnection();
|
||||
connection.setRequestProperty("User-Agent", "AdvancedServerList/" + core.getVersion());
|
||||
connection.connect();
|
||||
|
||||
return connection.getInputStream();
|
||||
}catch(IOException ex){
|
||||
core.getPlugin().getPluginLogger().warn("Error while connecting to %s for Favicon creation.", url);
|
||||
core.getPlugin().getPluginLogger().warn("Cause: %s", ex.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.profiles.handlers;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class FaviconHandler<F>{
|
||||
|
||||
private final AdvancedServerList<F> core;
|
||||
private final PluginLogger logger;
|
||||
private final ThreadPoolExecutor faviconThreadPool;
|
||||
private final Cache<String, CompletableFuture<F>> faviconCache;
|
||||
|
||||
private final Map<String, F> localFavicons = new HashMap<>();
|
||||
private final HttpClient client = HttpClient.newHttpClient();
|
||||
|
||||
public FaviconHandler(AdvancedServerList<F> core){
|
||||
this.core = core;
|
||||
this.logger = core.getPlugin().getPluginLogger();
|
||||
this.faviconThreadPool = createFaviconThreadPool();
|
||||
this.faviconCache = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(core.getFileHandler().getLong(1, 1, "faviconCacheTime"), TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
loadLocalFavicons();
|
||||
}
|
||||
|
||||
public F getFavicon(String input){
|
||||
if(localFavicons.size() > 0){
|
||||
logger.debug(FaviconHandler.class, "Current Local Favicons:");
|
||||
for(String key : localFavicons.keySet()){
|
||||
logger.debug(FaviconHandler.class, " - %s", key);
|
||||
}
|
||||
}
|
||||
|
||||
if(localFavicons.containsKey(input.toLowerCase(Locale.ROOT))){
|
||||
logger.debug(FaviconHandler.class, "Input matches local favicon Image. Returning it...");
|
||||
return localFavicons.get(input.toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
try{
|
||||
logger.debug(FaviconHandler.class, "Getting Favicon image from cache...");
|
||||
return faviconCache.get(input, () -> getFuture(input)).getNow(null);
|
||||
}catch(ExecutionException ex){
|
||||
logger.warn("Received ExecutionException while retrieving Favicon for '%s'.", ex, input);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void cleanCache(){
|
||||
faviconCache.invalidateAll();
|
||||
loadLocalFavicons();
|
||||
}
|
||||
|
||||
private CompletableFuture<F> getFuture(String input){
|
||||
if(input.toLowerCase(Locale.ROOT).startsWith("https://")){
|
||||
logger.debug(FaviconHandler.class, "Resolving URL '%s'...", input);
|
||||
return CompletableFuture.supplyAsync(() -> fromURL(core, input), this.faviconThreadPool);
|
||||
}else
|
||||
if(input.toLowerCase(Locale.ROOT).endsWith(".png")){
|
||||
logger.debug(FaviconHandler.class, "Resolving image file '%s'...", input);
|
||||
return CompletableFuture.completedFuture(localFavicons.get(input.toLowerCase(Locale.ROOT)));
|
||||
}else{
|
||||
logger.debug(FaviconHandler.class, "Resolving Name/UUID as https://mc-heads.net/avatar/%s/64...", input);
|
||||
return CompletableFuture.supplyAsync(() -> fromURL(core, "https://mc-heads.net/avatar/" + input + "/64"), this.faviconThreadPool);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadLocalFavicons(){
|
||||
this.localFavicons.clear();
|
||||
|
||||
logger.info("Loading local image files as Favicons...");
|
||||
|
||||
Path folder = core.getPlugin().getFolderPath().resolve("favicons");
|
||||
if(!Files.exists(folder)){
|
||||
try{
|
||||
Files.createDirectories(folder);
|
||||
logger.info("Created favicons folder.");
|
||||
}catch(IOException ex){
|
||||
logger.warn("Cannot create favicons folder. Encountered an IOException.", ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try(Stream<Path> pathStream = Files.list(folder)){
|
||||
pathStream.filter(Files::isRegularFile)
|
||||
.filter(file -> file.getFileName().toString().endsWith(".png"))
|
||||
.forEach(this::loadFile);
|
||||
}catch(IOException ex){
|
||||
logger.warn("Cannot load files from Favicons folder.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFile(Path path){
|
||||
try(InputStream stream = Files.newInputStream(path)){
|
||||
F favicon = createFavicon(stream);
|
||||
if(favicon == null){
|
||||
logger.warn("Cannot create Favicon from file '%s'. Received Favicon was null.", path.getFileName().toString());
|
||||
return;
|
||||
}
|
||||
|
||||
localFavicons.put(path.getFileName().toString().toLowerCase(Locale.ROOT), favicon);
|
||||
logger.info("Loaded file '%s' as Favicon.", path.getFileName().toString());
|
||||
}catch(IOException ex){
|
||||
logger.warn("Cannot create Favicon from file '%s'. Encountered IOException.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private F fromURL(AdvancedServerList<F> core, String url){
|
||||
try{
|
||||
logger.debug(FaviconHandler.class, "Creating Request for URL '%s'...", url);
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(new URI(url))
|
||||
.header("User-Agent", "AdvancedServerList-" + core.getPlugin().getLoader() + "/" + core.getVersion())
|
||||
.build();
|
||||
|
||||
try(InputStream stream = client.send(request, HttpResponse.BodyHandlers.ofInputStream()).body()){
|
||||
return createFavicon(stream);
|
||||
}catch(InterruptedException | IOException ex){
|
||||
logger.warn("Cannot create Favicon from URL '%s'. Encountered an Exception.", ex, url);
|
||||
return null;
|
||||
}
|
||||
}catch(URISyntaxException ex){
|
||||
logger.warn("Cannot create Favicon from URL '%s'. Encountered an IOException.", ex, url);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private F createFavicon(InputStream stream){
|
||||
try{
|
||||
logger.debug(FaviconHandler.class, "Creating BufferedImage from InputStream...");
|
||||
BufferedImage original = ImageIO.read(stream);
|
||||
if(original == null){
|
||||
logger.warn("Cannot create Favicon. Received null BufferedImage.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Don't waste resources resizing images already having right size.
|
||||
if(original.getWidth() == 64 && original.getHeight() == 64){
|
||||
logger.debug(FaviconHandler.class, "BufferedImage is 64x64 pixels. No resizing needed.");
|
||||
return core.getPlugin().createFavicon(original);
|
||||
}
|
||||
|
||||
logger.debug(FaviconHandler.class, "Resizing image to 64x64 pixels...");
|
||||
BufferedImage image = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D graphics2D = image.createGraphics();
|
||||
|
||||
graphics2D.drawImage(original, 0, 0, 64, 64, null);
|
||||
graphics2D.dispose();
|
||||
|
||||
logger.debug(FaviconHandler.class, "Image resized! Returning Favicon...");
|
||||
return core.getPlugin().createFavicon(image);
|
||||
}catch(Exception ex){
|
||||
logger.warn("Unable to create Favicon. Received Exception.", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ThreadPoolExecutor createFaviconThreadPool(){
|
||||
return new ThreadPoolExecutor(3, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024), r -> {
|
||||
Thread t = new Thread(r, "AdvancedServerList-FaviconThread");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,12 +23,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.profiles.players;
|
||||
package ch.andre601.advancedserverlist.core.profiles.handlers;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
import ch.andre601.advancedserverlist.core.objects.CachedPlayer;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonIOException;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import io.leangen.geantyref.TypeToken;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
@@ -38,7 +40,9 @@ import java.lang.reflect.Type;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PlayerHandler{
|
||||
|
||||
@@ -62,25 +66,24 @@ public class PlayerHandler{
|
||||
}
|
||||
|
||||
public void load(){
|
||||
if(!cache.toFile().exists()){
|
||||
if(!Files.exists(cache)){
|
||||
logger.info("No playercache.json file present. Skipping...");
|
||||
return;
|
||||
}
|
||||
|
||||
if(core.getFileHandler().getBoolean("disableCache")){
|
||||
logger.info("'disable_cache' is set to true. Skipping playercache.json loading...");
|
||||
logger.info("'disableCache' is set to true. Skipping playercache.json loading...");
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
BufferedReader reader = Files.newBufferedReader(cache);
|
||||
|
||||
|
||||
try(BufferedReader reader = Files.newBufferedReader(cache)){
|
||||
cachedPlayers = gson.fromJson(reader, listType);
|
||||
|
||||
reader.close();
|
||||
}catch(IOException ex){
|
||||
logger.warn("Encountered IOException while reading the playercache.json file!", ex);
|
||||
return;
|
||||
}catch(JsonIOException | JsonSyntaxException ex){
|
||||
logger.warn("Encountered JsonSyntaxException while parsing playercache.json file!", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
// In case Gson messes up... I guess.
|
||||
@@ -111,8 +114,6 @@ public class PlayerHandler{
|
||||
}catch(IOException ex){
|
||||
logger.warn("Encountered IOException while saving cached players to playercache.json!", ex);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void addPlayer(String ip, String name, UUID uuid){
|
||||
@@ -127,7 +128,7 @@ public class PlayerHandler{
|
||||
return getDefaultPlayer();
|
||||
|
||||
for(CachedPlayer player : cachedPlayers){
|
||||
if(player.getIp().equals(key))
|
||||
if(player.ip().equals(key))
|
||||
return player;
|
||||
}
|
||||
|
||||
@@ -136,7 +137,7 @@ public class PlayerHandler{
|
||||
|
||||
public CachedPlayer getCachedPlayer(UUID uuid){
|
||||
for(CachedPlayer player : cachedPlayers){
|
||||
if(player.getUuid().equals(uuid))
|
||||
if(player.uuid().equals(uuid))
|
||||
return player;
|
||||
}
|
||||
|
||||
@@ -150,7 +151,7 @@ public class PlayerHandler{
|
||||
|
||||
private boolean contains(String ip){
|
||||
for(CachedPlayer player : cachedPlayers){
|
||||
if(player.getIp().equals(ip))
|
||||
if(player.ip().equals(ip))
|
||||
return true;
|
||||
}
|
||||
|
||||