Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5729b9d861 | ||
|
|
3e94cf1377 | ||
|
|
c4e5d00122 | ||
|
|
5d819d1dbc | ||
|
|
8e7873fca2 | ||
|
|
b88c54d3ca | ||
|
|
713e49a11c | ||
|
|
eedbc88457 | ||
|
|
a0d0b71a2a | ||
|
|
639240bfb5 | ||
|
|
b1cd0d1058 | ||
|
|
c3b75ec1b7 | ||
|
|
4e8cb94908 | ||
|
|
c95c900a0e | ||
|
|
73f826b553 | ||
|
|
fea92bab5f | ||
|
|
1e9c042795 | ||
|
|
1f4672a6a2 | ||
|
|
8a60206af6 | ||
|
|
51b0123e5d | ||
|
|
c2c6dde058 | ||
|
|
53a1c55634 | ||
|
|
c5588f6981 | ||
|
|
bac9c7e0f4 | ||
|
|
f6c3f0f590 | ||
|
|
31f66caef7 | ||
|
|
f4c9fae031 | ||
|
|
9e212fa2ea | ||
|
|
3a25d6185c | ||
|
|
5cf4887ae8 | ||
|
|
e9bb64affa | ||
|
|
df34843b7f | ||
|
|
ee5b7d022a | ||
|
|
94fbcaa5f4 | ||
|
|
850becd183 | ||
|
|
2a488248af | ||
|
|
2e78b59c54 | ||
|
|
4919caefec | ||
|
|
158cac6bbc | ||
|
|
babbadb495 | ||
|
|
772e25f9f9 | ||
|
|
f649c18e51 | ||
|
|
6cdadae7b4 | ||
|
|
c36cb3b9ae | ||
|
|
1776de1ec0 | ||
|
|
0ae7f0990f | ||
|
|
53c4d738af | ||
|
|
97d6b9cf2f | ||
|
|
cd390ad95e | ||
|
|
5a204b2650 | ||
|
|
ffce2c7b8d | ||
|
|
1fb5eabcad | ||
|
|
7c53ea19e0 | ||
|
|
a614d1f2aa | ||
|
|
417303e804 | ||
|
|
9956b02736 | ||
|
|
43b3ddf81c | ||
|
|
f0a0c43351 | ||
|
|
734c124a65 | ||
|
|
741324d1c7 | ||
|
|
b28ef832c8 | ||
|
|
2b1a5aa45b | ||
|
|
c9581fe79c | ||
|
|
d385b68d2c | ||
|
|
dd82892d39 | ||
|
|
32239db674 | ||
|
|
16ece42116 | ||
|
|
137cf6e8da | ||
|
|
0ba3047acc | ||
|
|
de7d0ed3ca | ||
|
|
7c6bb28d39 | ||
|
|
0ce9ec0239 | ||
|
|
1aa196c3d5 | ||
|
|
5c7366198e | ||
|
|
cf7fe7236e | ||
|
|
e69c94da06 | ||
|
|
7cd3835785 | ||
|
|
8c36d27360 | ||
|
|
5329ed1b88 | ||
|
|
d66494f4dd | ||
|
|
d03ebf5fb1 | ||
|
|
c7ae2c130f | ||
|
|
017a1893c4 | ||
|
|
7a3af4cb26 | ||
|
|
0d05f2fe1d | ||
|
|
0acb097439 | ||
|
|
8a67fbead8 | ||
|
|
8f3252d66a | ||
|
|
7668a680c4 | ||
|
|
944e5a7262 | ||
|
|
d7cefb77ce | ||
|
|
e7a44711c0 | ||
|
|
96750ec5fd | ||
|
|
16500f29e4 | ||
|
|
25e433d0ed | ||
|
|
21430e9a4e | ||
|
|
de5455ab39 | ||
|
|
a52ca1d8be | ||
|
|
34d8048dd4 | ||
|
|
3b1e676888 | ||
|
|
9332a3e342 | ||
|
|
eacf227650 | ||
|
|
56131f4478 | ||
|
|
b362c34141 | ||
|
|
41718611e7 | ||
|
|
a92d6716e4 | ||
|
|
768509100d | ||
|
|
a64ad0cfa0 | ||
|
|
2abde6ef63 | ||
|
|
da2cf4d501 | ||
|
|
2a00634ce2 | ||
|
|
58108a8b19 | ||
|
|
6e7260d8f2 | ||
|
|
d75f43a4bd | ||
|
|
442c1539e9 | ||
|
|
a6188905dc | ||
|
|
81e0778825 | ||
|
|
afdcd11f7c | ||
|
|
4ca00cd8ad | ||
|
|
3b2d6b98b2 | ||
|
|
348d1fcacf | ||
|
|
4451d2382b | ||
|
|
3d410631ef | ||
|
|
e0ba710aed | ||
|
|
966d817f29 | ||
|
|
838416b1c5 | ||
|
|
23ee4a08ad | ||
|
|
baba7c7076 | ||
|
|
7e7228dd77 | ||
|
|
103bf4f1db | ||
|
|
0c7c8e56db | ||
|
|
cb3161a118 | ||
|
|
326fe7098a | ||
|
|
d79c54f7c9 | ||
|
|
1468febf72 | ||
|
|
afbcad6c3e | ||
|
|
79aefaa6a5 | ||
|
|
d6ac483a0e | ||
|
|
35df0d84ca | ||
|
|
45229bd6d2 | ||
|
|
7bfa88383d | ||
|
|
efaae58562 | ||
|
|
7dfdbb621d | ||
|
|
d2cd554fb1 | ||
|
|
69168efb01 | ||
|
|
55f94a8c27 | ||
|
|
e813bdaba4 | ||
|
|
b3f56aa398 | ||
|
|
87f0573162 | ||
|
|
e49fe391d0 | ||
|
|
d077efdc12 | ||
|
|
90fdaf628c | ||
|
|
9651011b19 | ||
|
|
9bacc04a68 | ||
|
|
515081e395 | ||
|
|
95fec87c12 | ||
|
|
778a415b4f | ||
|
|
db66945d99 | ||
|
|
cf4245842b | ||
|
|
d90084196b | ||
|
|
62b13a3cdf | ||
|
|
be6e5f8bfb | ||
|
|
aaf1439da8 | ||
|
|
b304252140 | ||
|
|
dda321f07b | ||
|
|
f487fd9cc1 | ||
|
|
5681e7a790 | ||
|
|
c3aa8bf536 | ||
|
|
1ce27b2244 | ||
|
|
74bce4c220 | ||
|
|
6605538b0f | ||
|
|
4aa9e946fd | ||
|
|
42ec17997e | ||
|
|
d2587e67f4 | ||
|
|
3f8f0e0ff7 | ||
|
|
b0dbbbfe82 | ||
|
|
6a32ea7826 | ||
|
|
a6bcc7036c | ||
|
|
639b32db23 | ||
|
|
53672243d3 | ||
|
|
3b7e177e4f | ||
|
|
6b43ef4dc3 | ||
|
|
665990aae9 | ||
|
|
70723ce35e | ||
|
|
b2d689306b | ||
|
|
6ae3f0aea6 | ||
|
|
9445e12f8d | ||
|
|
94da2bbd2d | ||
|
|
b5d0246bce | ||
|
|
48b1e53d13 | ||
|
|
5e7a59474d | ||
|
|
bfbc547712 | ||
|
|
2f8273d6a3 | ||
|
|
1f036727d8 | ||
|
|
bc07dadf03 | ||
|
|
ac0c754050 | ||
|
|
b2d664b5b9 | ||
|
|
58e9e55dc6 | ||
|
|
9b8e459b5c | ||
|
|
598343ebc7 | ||
|
|
c1fd983dab | ||
|
|
d05faf30dc | ||
|
|
79a96f30ca | ||
|
|
54ddf4bb4d | ||
|
|
f8b85ce9ed | ||
|
|
2f5c1db3c1 | ||
|
|
de7570ac6e | ||
|
|
41c7db5f27 | ||
|
|
ef0ef4e373 | ||
|
|
e9ab05618e | ||
|
|
a28e62f53c | ||
|
|
e82df2d1f7 | ||
|
|
16a2b05b2b | ||
|
|
5df3d6dd48 | ||
|
|
91a6149dac | ||
|
|
3f224504a1 | ||
|
|
437e155a46 | ||
|
|
8275069315 | ||
|
|
bd564a8638 | ||
|
|
591279f70b | ||
|
|
80e84aa41b | ||
|
|
9024c4dc71 | ||
|
|
bb0f389016 | ||
|
|
f8f8a45b1a | ||
|
|
0bae24bb66 | ||
|
|
2158caebe6 | ||
|
|
f9ea253da3 | ||
|
|
7c53ddf4f4 | ||
|
|
8bcc88d620 | ||
|
|
78b687587d | ||
|
|
168fe364b0 | ||
|
|
cd3f54dba6 | ||
|
|
79f15b2467 | ||
|
|
ade8ad5a33 | ||
|
|
6e343f1516 | ||
|
|
fee436fda6 | ||
|
|
27d4f5acd0 | ||
|
|
e18c70d446 | ||
|
|
0ca3ec62a9 | ||
|
|
461ae5d0aa | ||
|
|
5696a9417b | ||
|
|
0705101cd6 | ||
|
|
69ad3554cf | ||
|
|
7f1de13dcc | ||
|
|
c3e16eec31 | ||
|
|
207970a90b | ||
|
|
2628dbab56 | ||
|
|
3da9f8b400 | ||
|
|
ffd0a8c364 | ||
|
|
d9a7b244f9 | ||
|
|
f1fb5ef6ea | ||
|
|
d3406aad1c | ||
|
|
650bcc3235 | ||
|
|
0cd91c63bb | ||
|
|
e95a4634ec | ||
|
|
d0293ae15e | ||
|
|
f5c6185b96 | ||
|
|
61981db3cc | ||
|
|
52804adfdf | ||
|
|
4b185be8a4 | ||
|
|
46ac4ef5c1 | ||
|
|
00e2a8ebee | ||
|
|
c523da7477 | ||
|
|
01e586fa5e | ||
|
|
9322819538 | ||
|
|
7ef4a9a61b | ||
|
|
af2da61500 | ||
|
|
23d0626250 | ||
|
|
74dfdb3022 | ||
|
|
4a589619bd | ||
|
|
9bdd519246 | ||
|
|
cf4e08fe3e | ||
|
|
70bbb15c60 | ||
|
|
ee8e00f3a2 | ||
|
|
0280668f9b | ||
|
|
e959f478a0 | ||
|
|
f024b2c763 | ||
|
|
c0665b450e | ||
|
|
e50ed552b9 | ||
|
|
1601e41e9c | ||
|
|
f8b80f9e14 | ||
|
|
22bfe2be00 | ||
|
|
7e9c6b909c | ||
|
|
93ecdd736f | ||
|
|
abfbaf4498 | ||
|
|
ce3b80b05c | ||
|
|
9e3a77ad7d | ||
|
|
cdc2115cb3 | ||
|
|
5173f61c6c | ||
|
|
8b12cb2402 | ||
|
|
3ab48396af | ||
|
|
00b34c8b47 | ||
|
|
b06274638e | ||
|
|
cd3b2fe0b4 | ||
|
|
51d8ae6c42 | ||
|
|
1545be4709 | ||
|
|
7693e72728 | ||
|
|
b879110379 | ||
|
|
80900089bb | ||
|
|
df776f4e1c | ||
|
|
72afff8315 | ||
|
|
307384d1e1 | ||
|
|
84f31fad53 | ||
|
|
fe0774303a | ||
|
|
563173679b | ||
|
|
6e96e06109 | ||
|
|
dae25fd00a | ||
|
|
d2ca0cfae5 | ||
|
|
afaae368ac | ||
|
|
522ce7f932 | ||
|
|
3f3d460f2f | ||
|
|
3a03899688 | ||
|
|
e6d4264525 | ||
|
|
61466074fd | ||
|
|
4516157ddf | ||
|
|
82c65bf13c | ||
|
|
3c6e73671d | ||
|
|
c91a2c0d4e | ||
|
|
66b10656c3 | ||
|
|
b82a0ff0d9 | ||
|
|
2e02af5a67 | ||
|
|
5626b38390 | ||
|
|
415456447c | ||
|
|
c555a3c648 | ||
|
|
9ec0c1c4ce | ||
|
|
d3eb43db39 | ||
|
|
b4b4f6a9fc | ||
|
|
3217eee02b | ||
|
|
ea68fca70b | ||
|
|
a8ceb88b67 | ||
|
|
19d6ce28c4 | ||
|
|
05e61794a1 | ||
|
|
46e062eb8f | ||
|
|
2196456dd4 | ||
|
|
bfd1e2e2ad | ||
|
|
21af6df510 | ||
|
|
713062d1b4 | ||
|
|
673a134ab1 | ||
|
|
c777772a90 | ||
|
|
a43a9f8f19 | ||
|
|
d4a416f85e | ||
|
|
bc26ceae55 | ||
|
|
3d19a6688c | ||
|
|
03a8258ddc | ||
|
|
6df5015b0d | ||
|
|
f1f8aaaad2 | ||
|
|
151f5494fc | ||
|
|
f997fa0a89 | ||
|
|
2f1549eb8a | ||
|
|
77dba75c17 | ||
|
|
dfa72cb852 | ||
|
|
dc0cf6ebfc | ||
|
|
2c520e53c0 | ||
|
|
356cca9ba0 | ||
|
|
4e7b17a601 | ||
|
|
033f4bd6ab | ||
|
|
13edca3c4c | ||
|
|
a626dc5b28 | ||
|
|
7a60024bd0 | ||
|
|
cedb1e5748 | ||
|
|
8b0fb99bc5 | ||
|
|
31202a930a | ||
|
|
a105e3186b | ||
|
|
e2df79c428 | ||
|
|
01ecce170c | ||
|
|
3bb171cecf | ||
|
|
f54bbe26e1 | ||
|
|
ddede7abef | ||
|
|
2c6467f176 | ||
|
|
8b92f8fc4d | ||
|
|
7bf6d8de0f | ||
|
|
844a88d381 | ||
|
|
fcea4e90e3 | ||
|
|
6a97debdf7 | ||
|
|
07fa6cf0ec | ||
|
|
367f0f3501 | ||
|
|
16dcaef825 | ||
|
|
f780372fda | ||
|
|
a8f8c0d770 | ||
|
|
70e06afaf0 | ||
|
|
13e087eddd | ||
|
|
31af916f04 | ||
|
|
0c36667116 | ||
|
|
3417d7c0be | ||
|
|
96fa871204 | ||
|
|
aaacd9944f | ||
|
|
af44324cf1 | ||
|
|
77b6897bd4 | ||
|
|
9d963199ae | ||
|
|
580b87ebb5 | ||
|
|
efd5762162 | ||
|
|
62261ae6ed | ||
|
|
e52bfdf60b | ||
|
|
0152ba3e1a | ||
|
|
090787e292 | ||
|
|
25d14eeaae | ||
|
|
386d490596 | ||
|
|
207ce12452 | ||
|
|
b87703c36c | ||
|
|
41677d0635 | ||
|
|
dfdb2aa767 | ||
|
|
d14c5f80e8 | ||
|
|
bd9e376d08 | ||
|
|
578242078b | ||
|
|
f84842729d | ||
|
|
1daeecbbcf | ||
|
|
5dcab6c37c | ||
|
|
a146a10d53 | ||
|
|
75db0d5e96 | ||
|
|
644dd401d4 | ||
|
|
32d85c6be1 | ||
|
|
1b47af6714 | ||
|
|
4544696a54 | ||
|
|
a84848daee | ||
|
|
3f8dd90e28 | ||
|
|
f633741988 | ||
|
|
7e9ea9f470 | ||
|
|
c2c00dc47f | ||
|
|
027503efab | ||
|
|
7dd78cbbfc | ||
|
|
b27b8e78bd | ||
|
|
ad5b07feda | ||
|
|
d79784d1c0 | ||
|
|
ef84e85426 | ||
|
|
35782809ac | ||
|
|
3e3c4903e4 | ||
|
|
79b45fb29d | ||
|
|
46f40294e2 | ||
|
|
ca5ab5d221 | ||
|
|
b2e5d7ae28 | ||
|
|
649e9f07ef | ||
|
|
eaa86aa93b | ||
|
|
8a90619810 | ||
|
|
351db260ce | ||
|
|
bcc9d88e8c | ||
|
|
9c849157d3 | ||
|
|
f171c951e7 | ||
|
|
cae808e3fc | ||
|
|
ae8e6304f9 | ||
|
|
c975ba79b3 | ||
|
|
83fb13e1f2 | ||
|
|
586351e277 | ||
|
|
098b6be0f4 | ||
|
|
1c881d2832 | ||
|
|
e6f3d122c7 | ||
|
|
759474b344 | ||
|
|
6aef4c2de9 | ||
|
|
64d7c81c58 | ||
|
|
a070708323 | ||
|
|
e9ba86508c | ||
|
|
c1e3638534 | ||
|
|
f5260f3be1 | ||
|
|
51372aa8f4 | ||
|
|
025c9f4423 | ||
|
|
99c34dd343 | ||
|
|
cd91b799d6 | ||
|
|
5b7eb014a3 | ||
|
|
ad32952b97 | ||
|
|
4a1e552d5d | ||
|
|
ea1394dce9 | ||
|
|
317fa631e9 | ||
|
|
5dd26f6ab8 | ||
|
|
ba82df45ef | ||
|
|
d76339188d | ||
|
|
a9fb8f3ed6 | ||
|
|
18291a6865 | ||
|
|
83a1808ebc | ||
|
|
197967dea9 | ||
|
|
b25b65faba | ||
|
|
fafcd3ae18 | ||
|
|
daae88074f | ||
|
|
fbe019214d | ||
|
|
3e380342a7 | ||
|
|
fd2dbd2edb | ||
|
|
e969208ae9 | ||
|
|
77aa404e06 | ||
|
|
a6c1840858 | ||
|
|
762bd1d0fd | ||
|
|
02ce580433 | ||
|
|
c7b7feb476 | ||
|
|
0e145def83 | ||
|
|
03e32feb1d | ||
|
|
dbab282901 | ||
|
|
bd6b599267 | ||
|
|
1a63430c1e | ||
|
|
661f9e6ad4 | ||
|
|
6bd8d40ff4 | ||
|
|
16b74433ff | ||
|
|
1aaafe474f | ||
|
|
26a7697b0f | ||
|
|
ec6b653988 | ||
|
|
854eb67c54 | ||
|
|
00511f6e97 | ||
|
|
16eebb23a5 | ||
|
|
faae641c32 | ||
|
|
4fa542a242 | ||
|
|
2dc1f7ccc1 | ||
|
|
dd7b1a7392 | ||
|
|
a90d241cd2 | ||
|
|
452ac00ee7 | ||
|
|
f139270f0e | ||
|
|
b212b2af0f | ||
|
|
0d506379cf | ||
|
|
995793e66d | ||
|
|
f9ebc60d64 | ||
|
|
1d531a32e3 | ||
|
|
36340d9b9d | ||
|
|
d023e22934 | ||
|
|
260a0b4835 | ||
|
|
3f18aaaf5b | ||
|
|
3f0e7897d4 | ||
|
|
4bd35c810b | ||
|
|
b081be8f53 | ||
|
|
eb474d061b | ||
|
|
edbc893c65 | ||
|
|
d03dce9b29 | ||
|
|
de7d011216 | ||
|
|
4a2cb2078d | ||
|
|
05381bd538 | ||
|
|
51e561f2ba | ||
|
|
71a3bddc62 | ||
|
|
3eeffd5520 | ||
|
|
3590e4fa12 | ||
|
|
e1e7907d0f | ||
|
|
1e8f71092c | ||
|
|
1c12180fe3 | ||
|
|
7a41e27788 | ||
|
|
ddfc8763c4 | ||
|
|
086905ad53 | ||
|
|
4526e91e0f | ||
|
|
cfb74b277e | ||
|
|
d7c3a7d756 | ||
|
|
36c6af0a43 | ||
|
|
f9df11c9f8 | ||
|
|
8f42b63733 | ||
|
|
10604f4064 | ||
|
|
7a574513af | ||
|
|
deeea1578d | ||
|
|
ac379b1668 | ||
|
|
c636764f4b | ||
|
|
a823aebafd | ||
|
|
dd4c3c0eb3 | ||
|
|
23afe54e29 | ||
|
|
fab3731cf4 | ||
|
|
49a782d560 | ||
|
|
cdac325e71 | ||
|
|
88f4278f3f | ||
|
|
de4617e450 | ||
|
|
4c4573b1e0 | ||
|
|
f74f3ef766 | ||
|
|
806a920d32 | ||
|
|
c9db8cbc97 | ||
|
|
3e3cd112ec | ||
|
|
fcc7d7c6b6 | ||
|
|
cb31fc6f5a | ||
|
|
b26a614f87 | ||
|
|
eb6f4180bf | ||
|
|
02d6b1958b | ||
|
|
bf3c979819 | ||
|
|
b5af4b9c47 | ||
|
|
6b9cff2945 | ||
|
|
d17549ee68 | ||
|
|
8b73eb3c8c | ||
|
|
1ee9ae473d | ||
|
|
37006063cf | ||
|
|
76d97de029 | ||
|
|
cd8f6f4c22 | ||
|
|
ce14274e4c | ||
|
|
a5d777f4f1 | ||
|
|
f94e578694 | ||
|
|
4ff55ddf28 | ||
|
|
7c441e6ab5 | ||
|
|
2779f565a9 | ||
|
|
6d3cd0c505 | ||
|
|
cf95f11fdb | ||
|
|
679dbb8c0b | ||
|
|
f44e2759b9 | ||
|
|
29239cc4ed | ||
|
|
bcd5e77166 | ||
|
|
5d2fa03601 | ||
|
|
8f1a5e5a37 | ||
|
|
05fde902a4 | ||
|
|
d69e70c485 | ||
|
|
fba1e84171 | ||
|
|
530f0fd954 | ||
|
|
8bec42bb91 | ||
|
|
94982ff1e8 | ||
|
|
1f9795ed97 | ||
|
|
c6f95cb78a | ||
|
|
571f9f6b38 | ||
|
|
17d0802e41 | ||
|
|
6d7fbb8c25 | ||
|
|
d3d83e5e11 | ||
|
|
82fb9c90f4 | ||
|
|
6841a5a6e0 | ||
|
|
6f41cd4e3a | ||
|
|
379f36832e | ||
|
|
5d8a39cd22 | ||
|
|
9dba820840 | ||
|
|
58712ca5c5 | ||
|
|
e19e942799 | ||
|
|
03434130c4 | ||
|
|
99df25551a | ||
|
|
4f31e4ce6f | ||
|
|
ba097c538d | ||
|
|
aa52435591 | ||
|
|
f44194d24f | ||
|
|
0e2d760ea6 | ||
|
|
c1e7e6b3ff | ||
|
|
96d383a5a1 | ||
|
|
79ba7877bf | ||
|
|
86b9aaa7c6 | ||
|
|
0cb3c036af | ||
|
|
b1f7b75e2d | ||
|
|
e9bc72c165 | ||
|
|
93203249fe | ||
|
|
db06de243f | ||
|
|
2523ccb425 | ||
|
|
94972dad31 | ||
|
|
f8ac310259 | ||
|
|
5d8f5d81ed | ||
|
|
3111d7933d | ||
|
|
55f7e17d34 | ||
|
|
0e88a50deb | ||
|
|
83f351de6d | ||
|
|
b96d1cb1a9 | ||
|
|
517ff1df82 | ||
|
|
e8c7c4ee18 | ||
|
|
da44eac70e | ||
|
|
e85c121ee4 | ||
|
|
0c175999fc | ||
|
|
9c3f237f6d | ||
|
|
777545c94f | ||
|
|
dd197609e1 | ||
|
|
14ee306463 | ||
|
|
a635aa8c91 | ||
|
|
050ac3262a | ||
|
|
89aa309c90 | ||
|
|
e9b004c3f2 | ||
|
|
3ecca64a3d | ||
|
|
96dcea4e5b | ||
|
|
400f4709f5 | ||
|
|
5d4307128b | ||
|
|
65c96690f6 | ||
|
|
483d582cd1 | ||
|
|
6f14e17ae7 | ||
|
|
ff32cc1005 | ||
|
|
bcc18d7738 | ||
|
|
629079b438 | ||
|
|
bce03b68a8 | ||
|
|
841c32de0d | ||
|
|
f321ef0162 | ||
|
|
eca32d1858 | ||
|
|
f2c1f6e491 | ||
|
|
af31aac07e | ||
|
|
f8db3e7706 | ||
|
|
3d3ea2913c | ||
|
|
7b7042a856 | ||
|
|
c66c4f21f3 | ||
|
|
890a3f7b46 | ||
|
|
73b969bc51 | ||
|
|
55c93d8d59 | ||
|
|
e1ed8c1cb2 | ||
|
|
e2a91a461a | ||
|
|
8a454004a4 | ||
|
|
8df862530d | ||
|
|
4a1c93fb1b | ||
|
|
e795cf31eb | ||
|
|
e8d93d064f | ||
|
|
6a2a90b868 | ||
|
|
9cea1d3841 | ||
|
|
7453b43d6b | ||
|
|
7e30f937bd | ||
|
|
176a8ac42e | ||
|
|
f480ff51e1 | ||
|
|
e72b772cb9 | ||
|
|
c91faa899e | ||
|
|
e5c6d77e3d | ||
|
|
e8ad2c8266 | ||
|
|
d244f2816a | ||
|
|
73f3172504 | ||
|
|
d7c9f73898 | ||
|
|
2b6818180f | ||
|
|
c7c14799e3 | ||
|
|
59774347f6 | ||
|
|
e45f9a1e73 | ||
|
|
a44235dc10 | ||
|
|
52569a3588 | ||
|
|
602a8e27ea | ||
|
|
81a23953e5 | ||
|
|
9daf8f3a3e | ||
|
|
3d5b4501e3 | ||
|
|
67047e9877 | ||
|
|
7fcb0e78e3 | ||
|
|
8b8018245e | ||
|
|
b34143d4f5 | ||
|
|
a33c096cf4 | ||
|
|
0957edbcd8 | ||
|
|
70adf4e216 | ||
|
|
da42e8c137 | ||
|
|
f4eecda209 | ||
|
|
b934d1a856 | ||
|
|
bbec0aa064 | ||
|
|
ba1f620eee | ||
|
|
f0281adf8a | ||
|
|
c702f61c21 | ||
|
|
dec23e0412 | ||
|
|
782dcb8382 | ||
|
|
e401d1f8c1 | ||
|
|
5e6654b3ef | ||
|
|
25a9e8ef63 | ||
|
|
558e47a7da | ||
|
|
740896c304 | ||
|
|
62d9aa81a3 | ||
|
|
7220b633cf | ||
|
|
0908ddad5e | ||
|
|
d1549b1441 | ||
|
|
57f618e7e2 | ||
|
|
911c64f7ad | ||
|
|
fdeeb35fa9 | ||
|
|
c2c653e613 | ||
|
|
8fa7c1593d | ||
|
|
b2dc74951f | ||
|
|
099c45e4c4 | ||
|
|
e38f6648ee | ||
|
|
2d701aa8a8 | ||
|
|
1f32c22ebf | ||
|
|
610d86ad1b | ||
|
|
658ec8dd74 | ||
|
|
85b7210e7c | ||
|
|
98b99b37de | ||
|
|
bc3aaa4d28 | ||
|
|
a3d0342f28 | ||
|
|
6e057422b6 | ||
|
|
7ee65c6c21 | ||
|
|
b4b754e483 | ||
|
|
b7c0c8a312 | ||
|
|
cf6006f9d6 | ||
|
|
6bf44f6208 | ||
|
|
428dd3cd06 | ||
|
|
871e2efbe2 | ||
|
|
a5b45e2c82 | ||
|
|
9feb3dfac6 | ||
|
|
5961d0dca0 | ||
|
|
ea0f0f09f4 | ||
|
|
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 |
No files matched your search
@@ -0,0 +1,37 @@
|
||||
name: "Deploy Wiki"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags-ignore:
|
||||
- "**"
|
||||
paths:
|
||||
- ".forgejo/workflows/deploy_wiki.yml"
|
||||
- "docs/**"
|
||||
- "domains"
|
||||
- "properdocs.yml"
|
||||
- "requirements.txt"
|
||||
|
||||
jobs:
|
||||
deployWiki:
|
||||
runs-on: codeberg-small
|
||||
steps:
|
||||
- name: "Checkout Repository"
|
||||
uses: https://code.forgejo.org/actions/checkout@v7
|
||||
with:
|
||||
token: "${{ secrets.BOT_TOKEN }}"
|
||||
- name: "Setup Python 3.x"
|
||||
uses: https://code.forgejo.org/actions/setup-python@v7
|
||||
with:
|
||||
python-version: 3.x
|
||||
- name: "Install Dependencies"
|
||||
run: "pip install -r requirements.txt"
|
||||
- name: "Build Website"
|
||||
run: "properdocs build"
|
||||
- name: "Deploy Website"
|
||||
uses: https://code.forgejo.org/actions/git-pages@v2
|
||||
with:
|
||||
site: https://asl.andre601.ch
|
||||
token: ${{ secrets.FORGEJO_TOKEN }}
|
||||
source: site/
|
||||
@@ -0,0 +1,116 @@
|
||||
name: Pull Request Preview
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
paths:
|
||||
- ".forgejo/workflows/pr_preview.yml"
|
||||
- "docs/**"
|
||||
- "properdocs.yml"
|
||||
- "preview_domains"
|
||||
- "requirements.txt"
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BRANCH_NAME: "${{ github.event.repository.name }}/${{ github.event.pull_request.number }}"
|
||||
RUN_URL: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_number }}"
|
||||
|
||||
jobs:
|
||||
createPreview:
|
||||
runs-on: codeberg-small
|
||||
steps:
|
||||
#
|
||||
# Finding and updating comment in Pull request.
|
||||
# If not found, have one created instead.
|
||||
#
|
||||
- name: "Find Comment"
|
||||
id: find-comment
|
||||
uses: https://github.com/peter-evans/find-comment@v4
|
||||
with:
|
||||
issue-number: "${{ github.event.pull_request.number }}"
|
||||
comment-author: "Andre_601-Bot"
|
||||
- name: "Create or update Comment"
|
||||
id: comment
|
||||
uses: https://github.com/peter-evans/create-or-update-comment@v5
|
||||
with:
|
||||
body: |-
|
||||
## Pull request Preview
|
||||
|
||||
Creating Preview for Pull request. Please wait...
|
||||
comment-id: "${{ steps.find-comment.outputs.comment-id }}"
|
||||
issue-number: "${{ github.event.pull_request.number }}"
|
||||
token: "${{ secrets.BOT_TOKEN }}"
|
||||
edit-mode: replace
|
||||
#
|
||||
# Setup source and target repository.
|
||||
# Build website and push.
|
||||
#
|
||||
- name: "Checkout Source Repository"
|
||||
uses: https://code.forgejo.org/actions/checkout@v7
|
||||
with:
|
||||
token: ${{ secrets.BOT_TOKEN }}
|
||||
fetch-depth: 0
|
||||
ref: "${{ github.event.pull_request.head.sha }}"
|
||||
- name: "Checkout Target Repository"
|
||||
run: |
|
||||
git clone https://codeberg.org/Andre601/preview site
|
||||
cd site
|
||||
git switch ${{ env.BRANCH_NAME }} 2>/dev/null || git switch -c ${{ env.BRANCH_NAME }}
|
||||
- name: "Setup Python 3.x"
|
||||
uses: https://code.forgejo.org/actions/setup-python@v7
|
||||
with:
|
||||
python-version: 3.x
|
||||
- name: "Install Dependencies"
|
||||
run: "pip install -r requirements.txt"
|
||||
- name: "Build Website"
|
||||
run: "properdocs build"
|
||||
- name: "Commit and Push"
|
||||
run: |
|
||||
git config --global user.email "andre_601-bot@noreply.codeberg.org"
|
||||
git config --global user.name "PR Docs Preview Builder"
|
||||
mv preview_domains site/.domains
|
||||
cd site
|
||||
git remote add upstream https://${{ secrets.BOT_TOKEN }}@codeberg.org/Andre601/preview.git
|
||||
git add --all
|
||||
git commit -m "Update Preview for ${{ env.BRANCH_NAME }} ($( env TZ=Europe/Zurich date +"%d.%m.%Y %Z" ))"
|
||||
git push --set-upstream upstream ${{ env.BRANCH_NAME }} -f
|
||||
#
|
||||
# Update comment on success or failure.
|
||||
#
|
||||
- name: "Comment Success"
|
||||
uses: https://github.com/peter-evans/create-or-update-comment@v5
|
||||
with:
|
||||
body: |-
|
||||
## Pull request Preview
|
||||
|
||||
Pull request Preview created successfully!
|
||||
|
||||
| Name | Link |
|
||||
|---------|----------------------------------------------------------------------------------------------------------|
|
||||
| Commit | ${{ github.event.pull_request.head.sha }} |
|
||||
| Preview | https://preview.andre601.ch/@${{ github.event.repository.name }}~${{ github.event.pull_request.number }} |
|
||||
| Logs | ${{ env.RUN_URL }} |
|
||||
comment-id: "${{ steps.comment.outputs.comment-id }}"
|
||||
issue-number: "${{ github.event.pull_request.number }}"
|
||||
token: "${{ secrets.BOT_TOKEN }}"
|
||||
edit-mode: replace
|
||||
- name: "Comment Failure"
|
||||
if: ${{ failure() }}
|
||||
uses: https://github.com/peter-evans/create-or-update-comment@v5
|
||||
with:
|
||||
body: |-
|
||||
## Pull request Preview
|
||||
|
||||
Pull request Preview failed! Check the logs for details.
|
||||
|
||||
| Name | Link |
|
||||
|---------|-------------------------------------------|
|
||||
| Commit | ${{ github.event.pull_request.head.sha }} |
|
||||
| Logs | ${{ env.RUN_URL }} |
|
||||
comment-id: "${{ steps.comment.outputs.comment-id }}"
|
||||
issue-number: "${{ github.event.pull_request.number }}"
|
||||
token: "${{ secrets.BOT_TOKEN }}"
|
||||
edit-mode: replace
|
||||
@@ -16,13 +16,14 @@ body:
|
||||
label: Platform
|
||||
description: On what platform do you use the plugin?
|
||||
options:
|
||||
- BungeeCord
|
||||
- Other (Please specify)
|
||||
- Paper
|
||||
- Purpur
|
||||
- Spigot
|
||||
- Folia
|
||||
- Velocity
|
||||
- Waterfall
|
||||
- Other (Please specify)
|
||||
- Spigot (DISCONTINUED)
|
||||
- BungeeCord (DISCONTINUED)
|
||||
- Waterfall (DISCONTINUED)
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
@@ -32,6 +33,35 @@ body:
|
||||
Please provide the name of the platform (Proxy/Server) you use, if you selected `Other (Please specify)` in the above section.
|
||||
Leave empty otherwise.
|
||||
placeholder: SomeProxy
|
||||
- type: input
|
||||
attributes:
|
||||
label: Server/Proxy version
|
||||
description: |-
|
||||
Provide the Version of your Server/Proxy.
|
||||
For Paper and its forks, run `/version` and for Velocity run `/velocity info`
|
||||
placeholder: This server is running Paper version ...
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Plugin version
|
||||
description: |-
|
||||
Provide the Version of the Plugin.
|
||||
On Paper and its forks, run `/version AdvancedServerList` and on Velocity, run `/velocity plugins` and find the AdvancedServerList entry.
|
||||
placeholder: 5.9.0
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Logs
|
||||
description: |-
|
||||
Provide Logs from your Server/Proxy, uploaded to [mclo.gs](https://mclo.gs).
|
||||
|
||||
**DO NOT SKIP THIS!**
|
||||
Skipping this by writing "Not needed", "private", etc. will get your issue closed without warning!
|
||||
placeholder: 'https://mclo.gs/...'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What happened?
|
||||
@@ -49,7 +79,7 @@ body:
|
||||
placeholder: |-
|
||||
1. Do this
|
||||
2. ...
|
||||
3. Profit!
|
||||
3. Prof-... Bug!
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
@@ -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
|
||||
@@ -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://swiss.social/@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: 71 KiB |
|
After Width: | Height: | Size: 876 KiB |
|
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 156 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 16 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+ 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://docs.andre601.ch/AdvancedServerList
|
||||
@@ -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://docs.andre601.ch/AdvancedServerList/latest/profiles
|
||||
[conditions]: https://docs.andre601.ch/AdvancedServerList/latest/profiles/#conditions
|
||||
[placeholders]: https://docs.andre601.ch/AdvancedServerList/latest/profiles/placeholders
|
||||
[minimessage]: https://docs.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,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,49 +0,0 @@
|
||||
name: 'Deploy Site (Push)'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
GITHUB_REPO_INSTANCE: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python 3.7
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.x
|
||||
- name: Create/Get Cache
|
||||
uses: actions/cache@v4
|
||||
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: Build docs
|
||||
run: mkdocs build
|
||||
- name: Configure GitHub Pages
|
||||
uses: actions/configure-pages@v4
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: 'site/'
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
@@ -1,36 +1,63 @@
|
||||
when:
|
||||
- event: tag
|
||||
branch: master
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
|
||||
steps:
|
||||
createFiles:
|
||||
image: maven:3.9.6-eclipse-temurin-17
|
||||
secrets:
|
||||
- source: cb_token
|
||||
target: codeberg_api_token
|
||||
- source: modrinth_api_token
|
||||
target: modrinth_api_token
|
||||
- source: hangar_api_token
|
||||
target: hangar_api_token
|
||||
- source: discord_webhook_url
|
||||
target: discord_webhook_url
|
||||
- name: "Build Jar and Upload"
|
||||
image: maven:3.10.0-eclipse-temurin-25
|
||||
environment:
|
||||
CODEBERG_API_TOKEN:
|
||||
from_secret: cb_token
|
||||
MODRINTH_API_TOKEN:
|
||||
from_secret: modrinth_api_token
|
||||
HANGAR_API_TOKEN:
|
||||
from_secret: hangar_api_token
|
||||
DISCORD_WEBHOOK_URL:
|
||||
from_secret: discord_webhook_url
|
||||
commands:
|
||||
# Create the jar files.
|
||||
- mvn clean install
|
||||
# Move VersionUploader jar file
|
||||
- cd version-uploader/target/
|
||||
- cd version-uploader/target
|
||||
- mv VersionUploader.jar ../../
|
||||
- cd ../../
|
||||
# Execute Jar file
|
||||
- java -jar VersionUploader.jar --all
|
||||
uploadToCodeberg:
|
||||
- java -jar VersionUploader.jar upload all
|
||||
- name: "Upload Jars to Codeberg"
|
||||
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"
|
||||
- "addons/luckperms/target/AdvancedServerList-*.jar"
|
||||
api_key:
|
||||
from_secret: CB_TOKEN
|
||||
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
|
||||
- name: "Create Post on Bluesky"
|
||||
image: woodpeckerci/plugin-bluesky-post
|
||||
when:
|
||||
- status: ["success"]
|
||||
settings:
|
||||
identifier: andre601.ch
|
||||
app_password:
|
||||
from_secret: bluesky_app_password
|
||||
languages: en-US
|
||||
text: |-
|
||||
#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
|
||||
@@ -1,36 +0,0 @@
|
||||
when:
|
||||
- branch: master
|
||||
event: push
|
||||
path:
|
||||
include: ['requirements.txt', 'mkdocs.yml', 'docs/**']
|
||||
|
||||
variables:
|
||||
setup_git: &setup_git
|
||||
- apk add git
|
||||
- git config --global user.email "github@andre601.ch"
|
||||
- git config --global user.name "ASL Docs Builder"
|
||||
|
||||
steps:
|
||||
cloneTargetRepo:
|
||||
image: alpine:3.18.4
|
||||
commands:
|
||||
- chmod -R a+w .
|
||||
- <<: *setup_git
|
||||
- git clone -b pages https://codeberg.org/Andre601/AdvancedServerList.git site
|
||||
- chmod -R a+w site
|
||||
buildDocs:
|
||||
image: woodpeckerci/plugin-mkdocs:latest
|
||||
settings:
|
||||
pip_install_file: requirements.txt
|
||||
commitAndPush:
|
||||
image: alpine:3.18.4
|
||||
secrets:
|
||||
- cb_token
|
||||
commands:
|
||||
- <<: *setup_git
|
||||
- mv domains site/.domains
|
||||
- cd site
|
||||
- git remote set-url origin https://$CB_TOKEN@codeberg.org/Andre601/AdvancedServerList.git
|
||||
- git add --all
|
||||
- git commit -m "Update AdvancedServerList Docs ($( env TZ=Europe/Zurich date +"%d.%m.%Y %Z" )) [SKIP CI]"
|
||||
- git push
|
||||
@@ -1,121 +1,122 @@
|
||||
# AdvancedServerList
|
||||
|
||||
AdvancedServerList is a plugin that allows 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">
|
||||
<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://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?
|
||||
## Server List Profiles
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
> [*Read More*][profiles]
|
||||
### Priority and Conditions
|
||||
|
||||
### 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.
|
||||
|
||||
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 it automatically true.
|
||||
|
||||
> [*Read More*][conditions]
|
||||
> *Read more about [Priority](https://asl.andre601.ch/profiles/options/priority) and [Conditions](https://asl.andre601.ch/profiles/options/condition)*
|
||||
|
||||
### Placeholders
|
||||
|
||||
Placeholders can be used within conditions to further customize them. They can also be used in any other text option 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.
|
||||
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.
|
||||
|
||||
When using the SpigotMC/PaperMC version, or while [PAPIProxyBridge][papiproxybridge] is installed on your proxy, can you also use placeholders from [PlaceholderAPI][placeholderapi] using its `%<identifier>_<values>%` placeholder format. PlaceholderAPI needs to be installed on the server (not proxy) for that.
|
||||
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.
|
||||
|
||||
If you're a plugin dev can you even add your own custom placeholders through AdvancedServerList's own 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.
|
||||
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*][minimessage]
|
||||
> *Read more about [Formatting](https://asl.andre601.ch/profiles/formatting/)*
|
||||
|
||||
## Dependencies
|
||||
|
||||
All listed dependencies are optional.
|
||||
AdvancedServerList soft-depends on the following plugins, meaning their inclusion is not required.
|
||||
|
||||
| Dependency | Platforms |
|
||||
|-------------------|----------------------|
|
||||
| [PlaceholderAPI] | Paper |
|
||||
| [ViaVersion] | Paper |
|
||||
| [PAPIProxyBridge] | BungeeCord, Velocity |
|
||||
| [Maintenance] | All |
|
||||
| Plugin | Supported Platforms |
|
||||
|-------------------------------------------------------------------------|----------------------------------------|
|
||||
| [PlaceholderAPI](https://modrinth.com/plugin/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>
|
||||
|
||||
English Video by KasaiSora
|
||||
[](https://youtube.com/watch?v=9cU0uUWHOO0)
|
||||
|
||||
Spanish Video by Ajneb97
|
||||
[](https://youtube.com/watch?v=rIbljm_4HVI)
|
||||
|
||||
Indonesian Video by faajrikha
|
||||
[](https://www.youtube.com/watch?v=-p938rOmqy4)
|
||||
|
||||
German Video by Finni Gamer
|
||||
[](https://www.youtube.com/watch?v=1Kh3cPMDd0c)
|
||||
|
||||
</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>
|
||||
<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://asl.andre601.ch/profiles/
|
||||
[conditions]: https://asl.andre601.ch/profiles/#condition
|
||||
[placeholders]: https://asl.andre601.ch/profiles/placeholders/
|
||||
[minimessage]: https://asl.andre601.ch/profiles/formatting/
|
||||
|
||||
[modrinth]: https://modrinth.com/plugin/advancedserverlist
|
||||
|
||||
[placeholderapi]: https://hangar.papermc.io/HelpChat/PlaceholderAPI
|
||||
[viaversion]: https://hangar.papermc.io/ViaVersion/ViaVersion
|
||||
[papiproxybridge]: https://hangar.papermc.io/William278/PAPIProxyBridge
|
||||
[maintenance]: https://hangar.papermc.io/kennytv/Maintenance
|
||||
|
||||
[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
|
||||
<br>
|
||||
<a href="https://swiss.social/@andre_601" target="_blank">
|
||||
<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,60 @@
|
||||
# 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 |
|
||||
|---------------|-----------|
|
||||
| v5 | Yes |
|
||||
| v4 | Yes* |
|
||||
| v3 | No** |
|
||||
| v2 | No |
|
||||
| v1 | No |
|
||||
|
||||
\*Only receives support but no updates or bug fixes.
|
||||
\*\*Support ended on December 31st, 2024.
|
||||
|
||||
## Supported Server/Proxy Versions
|
||||
|
||||
| Server/Proxy Version* | Supported |
|
||||
|-----------------------|-----------|
|
||||
| 26.2 | Yes |
|
||||
| 26.1.x | Yes |
|
||||
| 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 | No |
|
||||
| Waterfall | No |
|
||||
| Velocity | Yes |
|
||||
@@ -40,8 +40,8 @@
|
||||
<version>${plugin.version}</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>16</maven.compiler.source>
|
||||
<maven.compiler.target>16</maven.compiler.target>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
</repository>
|
||||
<repository>
|
||||
<id>papermc</id>
|
||||
<url>https://papermc.io/repo/repository/maven-public/</url>
|
||||
<url>https://repo.papermc.io/repository/maven-public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>libertybans</id>
|
||||
@@ -62,30 +62,16 @@
|
||||
|
||||
<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.20-R0.1-SNAPSHOT</version>
|
||||
<version>[26.1.2.build,)</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.velocitypowered</groupId>
|
||||
<artifactId>velocity-api</artifactId>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
<version>4.3.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -99,7 +85,13 @@
|
||||
<dependency>
|
||||
<groupId>space.arim.libertybans</groupId>
|
||||
<artifactId>bans-api</artifactId>
|
||||
<version>1.1.0-RC1</version>
|
||||
<version>1.1.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.gitlab.ruany</groupId>
|
||||
<artifactId>LiteBansAPI</artifactId>
|
||||
<version>0.6.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -111,19 +103,12 @@
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>AdvancedServerList-BanPlugins-Addon-${plugin.version}</finalName>
|
||||
<finalName>AdvancedServerList-BanPlugins-Addon-${plugin.version}${project.build.build_id}</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.12.1</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -23,35 +23,48 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.banplugins.bungeecord;
|
||||
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;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public enum BungeeCordBanPlugins{
|
||||
public enum BanPluginsList{
|
||||
|
||||
ADVANCED_BAN("AdvancedBan", new AdvancedBanPlaceholders()),
|
||||
LIBERTY_BANS("LibertyBans", new LibertyBansPlaceholders());
|
||||
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 plugin;
|
||||
private final String name;
|
||||
private final PlaceholderProvider placeholderProvider;
|
||||
private final boolean paper, bungeecord, velocity;
|
||||
|
||||
BungeeCordBanPlugins(String plugin, PlaceholderProvider placeholderProvider){
|
||||
this.plugin = plugin;
|
||||
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 static Map<String, PlaceholderProvider> getBanPlugins(){
|
||||
Map<String, PlaceholderProvider> content = new HashMap<>();
|
||||
|
||||
for(BungeeCordBanPlugins bungeeCordBanPlugins : BungeeCordBanPlugins.values()){
|
||||
content.put(bungeeCordBanPlugins.plugin, bungeeCordBanPlugins.placeholderProvider);
|
||||
}
|
||||
|
||||
return content;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* 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.api.PlaceholderProvider;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
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;
|
||||
Map<String, PlaceholderProvider> banPlugins = BungeeCordBanPlugins.getBanPlugins();
|
||||
AdvancedServerListAPI api = AdvancedServerListAPI.get();
|
||||
|
||||
for(Map.Entry<String, PlaceholderProvider> entry : banPlugins.entrySet()){
|
||||
if(getProxy().getPluginManager().getPlugin(entry.getKey()) != null){
|
||||
getLogger().info("Registering Placeholders for " + entry.getKey() + "...");
|
||||
api.addPlaceholderProvider(entry.getValue());
|
||||
getLogger().info("Placeholder successfully registered!");
|
||||
|
||||
loaded++;
|
||||
}
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* 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.PlaceholderProvider;
|
||||
import ch.andre601.advancedserverlist.banplugins.placeholders.AdvancedBanPlaceholders;
|
||||
import ch.andre601.advancedserverlist.banplugins.placeholders.LibertyBansPlaceholders;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public enum PaperBanPlugins{
|
||||
|
||||
ADVANCED_BAN("AdvancedBan", new AdvancedBanPlaceholders()),
|
||||
LIBERTY_BANS("LibertyBans", new LibertyBansPlaceholders());
|
||||
|
||||
private final String plugin;
|
||||
private final PlaceholderProvider placeholderProvider;
|
||||
|
||||
PaperBanPlugins(String plugin, PlaceholderProvider placeholderProvider){
|
||||
this.plugin = plugin;
|
||||
this.placeholderProvider = placeholderProvider;
|
||||
}
|
||||
|
||||
public static Map<String, PlaceholderProvider> getBanPlugins(){
|
||||
Map<String, PlaceholderProvider> content = new HashMap<>();
|
||||
|
||||
for(PaperBanPlugins paperBanPlugins : PaperBanPlugins.values()){
|
||||
content.put(paperBanPlugins.plugin, paperBanPlugins.placeholderProvider);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
@@ -26,11 +26,9 @@
|
||||
package ch.andre601.advancedserverlist.banplugins.paper;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.AdvancedServerListAPI;
|
||||
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
|
||||
import ch.andre601.advancedserverlist.banplugins.BanPluginsList;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class PaperBanPluginsAddon extends JavaPlugin{
|
||||
|
||||
@Override
|
||||
@@ -53,14 +51,16 @@ public class PaperBanPluginsAddon extends JavaPlugin{
|
||||
|
||||
private int loadPlaceholderProviders(){
|
||||
int loaded = 0;
|
||||
Map<String, PlaceholderProvider> banPlugins = PaperBanPlugins.getBanPlugins();
|
||||
AdvancedServerListAPI api = AdvancedServerListAPI.get();
|
||||
|
||||
for(Map.Entry<String, PlaceholderProvider> entry : banPlugins.entrySet()){
|
||||
if(getServer().getPluginManager().isPluginEnabled(entry.getKey())){
|
||||
getLogger().info("Registering Placeholders for " + entry.getKey() + "...");
|
||||
api.addPlaceholderProvider(entry.getValue());
|
||||
getLogger().info("Placeholder successfully registered!");
|
||||
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++;
|
||||
}
|
||||
|
||||
@@ -30,11 +30,9 @@ import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.banplugins.providers.AdvancedBanProvider;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class AdvancedBanPlaceholders extends PlaceholderProvider{
|
||||
|
||||
private final AdvancedBanProvider provider = new AdvancedBanProvider();
|
||||
private AdvancedBanProvider provider = null;
|
||||
|
||||
public AdvancedBanPlaceholders(){
|
||||
super("advancedban");
|
||||
@@ -42,19 +40,19 @@ public class AdvancedBanPlaceholders extends PlaceholderProvider{
|
||||
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
|
||||
String[] args = placeholder.split("\\s");
|
||||
if(provider == null)
|
||||
provider = new AdvancedBanProvider();
|
||||
|
||||
String[] args = placeholder.split("\\s", 2);
|
||||
|
||||
return switch(args[0]) {
|
||||
// Mute-related placeholders
|
||||
case "isMuted" -> String.valueOf(provider.muted(player));
|
||||
case "isMuted" -> provider.muted(player);
|
||||
case "muteReason" -> provider.muteReason(player);
|
||||
case "muteDuration" -> {
|
||||
if(args.length == 1)
|
||||
yield provider.muteDuration(player);
|
||||
|
||||
if(args.length > 2)
|
||||
yield null;
|
||||
|
||||
boolean fromStart = Boolean.getBoolean(args[1]);
|
||||
yield provider.muteDuration(player, fromStart);
|
||||
}
|
||||
@@ -62,21 +60,16 @@ public class AdvancedBanPlaceholders extends PlaceholderProvider{
|
||||
if(args.length == 1)
|
||||
yield provider.muteExpirationDate(player);
|
||||
|
||||
String pattern = String.join(" ", Arrays.copyOfRange(args, 1, args.length));
|
||||
|
||||
yield provider.banExpirationDate(player, pattern);
|
||||
yield provider.banExpirationDate(player, args[1]);
|
||||
}
|
||||
|
||||
// Ban-related placeholders
|
||||
case "isBanned" -> String.valueOf(provider.banned(player));
|
||||
case "isBanned" -> provider.banned(player);
|
||||
case "banReason" -> provider.banReason(player);
|
||||
case "banDuration" -> {
|
||||
if(args.length == 1)
|
||||
yield provider.banDuration(player);
|
||||
|
||||
if(args.length > 2)
|
||||
yield null;
|
||||
|
||||
boolean fromStart = Boolean.getBoolean(args[1]);
|
||||
yield provider.banDuration(player, fromStart);
|
||||
}
|
||||
@@ -84,9 +77,7 @@ public class AdvancedBanPlaceholders extends PlaceholderProvider{
|
||||
if(args.length == 1)
|
||||
yield provider.banExpirationDate(player);
|
||||
|
||||
String pattern = String.join(" ", Arrays.copyOfRange(args, 1, args.length));
|
||||
|
||||
yield provider.banExpirationDate(player, pattern);
|
||||
yield provider.banExpirationDate(player, args[1]);
|
||||
}
|
||||
|
||||
// Unknown/Invalid placeholder.
|
||||
|
||||
@@ -30,11 +30,9 @@ import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.banplugins.providers.LibertyBansProvider;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class LibertyBansPlaceholders extends PlaceholderProvider{
|
||||
|
||||
private final LibertyBansProvider provider = LibertyBansProvider.create();
|
||||
private LibertyBansProvider provider = null;
|
||||
|
||||
public LibertyBansPlaceholders(){
|
||||
super("libertybans");
|
||||
@@ -42,31 +40,30 @@ public class LibertyBansPlaceholders extends PlaceholderProvider{
|
||||
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
|
||||
String[] args = placeholder.split("\\.");
|
||||
if(provider == null)
|
||||
provider = LibertyBansProvider.create();
|
||||
|
||||
String[] args = placeholder.split("\\s", 2);
|
||||
|
||||
return switch(args[0]){
|
||||
// Mute-related placeholders
|
||||
case "isMuted" -> String.valueOf(provider.muted(player));
|
||||
case "isMuted" -> provider.muted(player);
|
||||
case "muteReason" -> provider.muteReason(player);
|
||||
case "muteExpiration" -> {
|
||||
if(args.length == 1)
|
||||
yield provider.muteExpirationDate(player);
|
||||
|
||||
String pattern = String.join(" ", Arrays.copyOfRange(args, 1, args.length));
|
||||
|
||||
yield provider.muteExpirationDate(player, pattern);
|
||||
yield provider.muteExpirationDate(player, args[1]);
|
||||
}
|
||||
|
||||
// Ban-related placeholders
|
||||
case "isBanned" -> String.valueOf(provider.banned(player));
|
||||
case "isBanned" -> provider.banned(player);
|
||||
case "banReason" -> provider.banReason(player);
|
||||
case "banExpiration" -> {
|
||||
if(args.length == 1)
|
||||
yield provider.banExpirationDate(player);
|
||||
|
||||
String pattern = String.join(" ", Arrays.copyOfRange(args, 1, args.length));
|
||||
|
||||
yield provider.banExpirationDate(player, pattern);
|
||||
yield provider.banExpirationDate(player, args[1]);
|
||||
}
|
||||
|
||||
// Unknown/Invalid placeholder
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,40 +31,18 @@ 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 boolean muted(GenericPlayer player){
|
||||
return getAPI().isMuted(noDashesUUID(player.getUUID()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean banned(GenericPlayer player){
|
||||
return getAPI().isBanned(noDashesUUID(player.getUUID()));
|
||||
public String banned(GenericPlayer player){
|
||||
return String.valueOf(getAPI().isBanned(noDashesUUID(player.getUUID())));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -136,4 +114,24 @@ public class AdvancedBanProvider implements PunishmentProvider{
|
||||
|
||||
return ban.getDuration(fromStart);
|
||||
}
|
||||
|
||||
private PunishmentManager getAPI(){
|
||||
return PunishmentManager.get();
|
||||
}
|
||||
|
||||
private String noDashesUUID(UUID uuid){
|
||||
return hexString(uuid.getMostSignificantBits()) + hexString(uuid.getLeastSignificantBits());
|
||||
}
|
||||
|
||||
private Punishment mute(GenericPlayer player){
|
||||
return getAPI().getMute(noDashesUUID(player.getUUID()));
|
||||
}
|
||||
|
||||
private Punishment ban(GenericPlayer player){
|
||||
return getAPI().getBan(noDashesUUID(player.getUUID()));
|
||||
}
|
||||
|
||||
private String hexString(long unsigned){
|
||||
return Strings.padStart(UnsignedLongs.toString(unsigned, 16), 16, '0');
|
||||
}
|
||||
}
|
||||
@@ -53,13 +53,13 @@ public class LibertyBansProvider implements PunishmentProvider{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean muted(GenericPlayer player){
|
||||
return punishment(player.getUUID(), PunishmentType.MUTE) != null;
|
||||
public String muted(GenericPlayer player){
|
||||
return String.valueOf(punishment(player.getUUID(), PunishmentType.MUTE) != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean banned(GenericPlayer player){
|
||||
return punishment(player.getUUID(), PunishmentType.BAN) != null;
|
||||
public String banned(GenericPlayer player){
|
||||
return String.valueOf(punishment(player.getUUID(), PunishmentType.BAN) != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -32,9 +32,9 @@ import java.util.Date;
|
||||
|
||||
public interface PunishmentProvider{
|
||||
|
||||
boolean muted(GenericPlayer player);
|
||||
String muted(GenericPlayer player);
|
||||
|
||||
boolean banned(GenericPlayer player);
|
||||
String banned(GenericPlayer player);
|
||||
|
||||
String muteReason(GenericPlayer player);
|
||||
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* 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.PlaceholderProvider;
|
||||
import ch.andre601.advancedserverlist.banplugins.paper.PaperBanPlugins;
|
||||
import ch.andre601.advancedserverlist.banplugins.placeholders.LibertyBansPlaceholders;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public enum VelocityBanPlugins{
|
||||
|
||||
LIBERTY_BANS("libertybans", new LibertyBansPlaceholders());
|
||||
|
||||
private final String plugin;
|
||||
private final PlaceholderProvider placeholderProvider;
|
||||
|
||||
VelocityBanPlugins(String plugin, PlaceholderProvider placeholderProvider){
|
||||
this.plugin = plugin;
|
||||
this.placeholderProvider = placeholderProvider;
|
||||
}
|
||||
|
||||
public static Map<String, PlaceholderProvider> getBanPlugins(){
|
||||
Map<String, PlaceholderProvider> content = new HashMap<>();
|
||||
|
||||
for(VelocityBanPlugins velocityBanPlugins : VelocityBanPlugins.values()){
|
||||
content.put(velocityBanPlugins.plugin, velocityBanPlugins.placeholderProvider);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
@@ -26,15 +26,15 @@
|
||||
package ch.andre601.advancedserverlist.banplugins.velocity;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.AdvancedServerListAPI;
|
||||
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
|
||||
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 net.md_5.bungee.api.ProxyServer;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Locale;
|
||||
|
||||
public class VelocityBanPluginsAddon{
|
||||
|
||||
@@ -49,7 +49,7 @@ public class VelocityBanPluginsAddon{
|
||||
|
||||
@Subscribe
|
||||
public void init(ProxyInitializeEvent event){
|
||||
if(proxy.getPluginManager().getPlugin("advancedserverlist") == null){
|
||||
if(!proxy.getPluginManager().isLoaded("advancedserverlist")){
|
||||
logger.warn("AdvancedServerList is not enabled. This plugin requires it to function!");
|
||||
return;
|
||||
}
|
||||
@@ -64,14 +64,16 @@ public class VelocityBanPluginsAddon{
|
||||
|
||||
private int loadPlaceholderProviders(){
|
||||
int loaded = 0;
|
||||
Map<String, PlaceholderProvider> banPlugins = VelocityBanPlugins.getBanPlugins();
|
||||
AdvancedServerListAPI api = AdvancedServerListAPI.get();
|
||||
|
||||
for(Map.Entry<String, PlaceholderProvider> entry : banPlugins.entrySet()){
|
||||
if(proxy.getPluginManager().getPlugin(entry.getKey()) != null){
|
||||
logger.info("Registering placeholders for {}...", entry.getKey());
|
||||
api.addPlaceholderProvider(entry.getValue());
|
||||
logger.info("Placeholder successfully registered!");
|
||||
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++;
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
name: 'ASLBanPluginsAddon'
|
||||
author: 'Andre_601'
|
||||
version: '${plugin.version}'
|
||||
description: 'AdvancedBan Add-on for AdvancedServerList to add placeholders to use.'
|
||||
|
||||
softDepends:
|
||||
- AdvancedServerList
|
||||
- AdvancedBan
|
||||
- LibertyBans
|
||||
|
||||
main: 'ch.andre601.advancedserverlist.banplugins.bungeecord.BungeeCordBanPluginsAddon'
|
||||
@@ -2,7 +2,7 @@ name: 'ASLBanPluginsAddon'
|
||||
author: 'Andre_601'
|
||||
version: '${plugin.version}'
|
||||
|
||||
main: 'ch.andre601.advancedserverlist.banplugins.paper.AdvancedBanPlaceholders'
|
||||
main: 'ch.andre601.advancedserverlist.banplugins.paper.PaperBanPluginsAddon'
|
||||
|
||||
api-version: '1.19'
|
||||
|
||||
@@ -11,9 +11,12 @@ dependencies:
|
||||
AdvancedServerList:
|
||||
required: false
|
||||
load: BEFORE
|
||||
AdvancedBans:
|
||||
AdvancedBan:
|
||||
required: false
|
||||
load: BEFORE
|
||||
LibertyBans:
|
||||
required: false
|
||||
load: BEFORE
|
||||
LiteBans:
|
||||
required: false
|
||||
load: BEFORE
|
||||
@@ -14,6 +14,10 @@
|
||||
{
|
||||
"id": "libertybans",
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"id": "litebans",
|
||||
"optional": true
|
||||
}
|
||||
],
|
||||
"main": "ch.andre601.advancedserverlist.banplugins.velocity.VelocityBanPluginsAddon"
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
<?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>luckperms</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>${plugin.version}</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</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>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<!-- Proxy/Server -->
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.21-R0.4</version>
|
||||
<type>jar</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.21-R0.4</version>
|
||||
<type>javadoc</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>[26.1.2.build,)</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.velocitypowered</groupId>
|
||||
<artifactId>velocity-api</artifactId>
|
||||
<version>4.3.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Plugin APIs -->
|
||||
<dependency>
|
||||
<groupId>ch.andre601.asl-api</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>${api.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.luckperms</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>5.5</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>AdvancedServerList-LuckPerms-Addon-${plugin.version}${project.build.build_id}</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<filtering>true</filtering>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,44 @@
|
||||
package ch.andre601.advancedserverlist.luckperms.paper;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.AdvancedServerListAPI;
|
||||
import ch.andre601.advancedserverlist.api.exceptions.InvalidPlaceholderProviderException;
|
||||
import ch.andre601.advancedserverlist.luckperms.placeholders.LuckPermsPlaceholder;
|
||||
import net.luckperms.api.LuckPerms;
|
||||
import net.luckperms.api.LuckPermsProvider;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class LuckPermsAddon extends JavaPlugin{
|
||||
|
||||
@Override
|
||||
public void onEnable(){
|
||||
if(!this.getServer().getPluginManager().isPluginEnabled("AdvancedServerList")){
|
||||
this.getSLF4JLogger().warn("AdvancedServerList not enabled. This plugin is required!");
|
||||
this.getServer().getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!this.getServer().getPluginManager().isPluginEnabled("LuckPerms")){
|
||||
this.getSLF4JLogger().warn("LuckPerms not enabled. This plugin is required!");
|
||||
this.getServer().getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
|
||||
LuckPerms luckPerms;
|
||||
try{
|
||||
luckPerms = LuckPermsProvider.get();
|
||||
}catch(IllegalStateException ex){
|
||||
this.getSLF4JLogger().error("Unable to obtain LuckPerms API instance! Disabling plugin...");
|
||||
this.getServer().getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
|
||||
this.getSLF4JLogger().info("Registering LuckPerms Placeholder...");
|
||||
try{
|
||||
AdvancedServerListAPI.get().addPlaceholderProvider(new LuckPermsPlaceholder(luckPerms));
|
||||
this.getSLF4JLogger().info("Successfully registered Placeholder!");
|
||||
}catch(InvalidPlaceholderProviderException ex){
|
||||
this.getSLF4JLogger().warn("Couldn't register Placeholder. Disabling Plugin...");
|
||||
this.getServer().getPluginManager().disablePlugin(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package ch.andre601.advancedserverlist.luckperms.placeholders;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import net.luckperms.api.LuckPerms;
|
||||
import net.luckperms.api.model.user.User;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
public class LuckPermsPlaceholder extends PlaceholderProvider{
|
||||
|
||||
private final LuckPerms luckPerms;
|
||||
|
||||
private final HashMap<UUID, CacheValue> cache = new HashMap<>();
|
||||
|
||||
public LuckPermsPlaceholder(LuckPerms luckPerms){
|
||||
super("luckperms");
|
||||
this.luckPerms = luckPerms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String parsePlaceholder(@NonNull String placholder, @NonNull GenericPlayer player, @NonNull GenericServer server){
|
||||
if(luckPerms == null)
|
||||
return "ERR: LuckPerms not available.";
|
||||
|
||||
CacheValue cacheValue = cache.get(player.getUUID());
|
||||
User user;
|
||||
if(cacheValue == null || cacheValue.expired()){
|
||||
// Can cause lag if on main thread.
|
||||
// But the alternative would be a lot more annoying and error-prone.
|
||||
user = luckPerms.getUserManager().loadUser(player.getUUID()).join();
|
||||
cache.put(player.getUUID(), new CacheValue(System.currentTimeMillis(), user));
|
||||
}else{
|
||||
user = cacheValue.user();
|
||||
}
|
||||
|
||||
if(user == null)
|
||||
return "ERR: Player not found.";
|
||||
|
||||
String[] parts = placholder.split("\\s+");
|
||||
if(parts.length == 1)
|
||||
return "ERR: Missing argument for placeholder " + parts[0] + ".";
|
||||
|
||||
return switch(parts[0]){
|
||||
case "hasGroup" -> String.valueOf(
|
||||
user.getCachedData().getPermissionData().checkPermission("group." + parts[1])
|
||||
);
|
||||
case "hasPermission" -> String.valueOf(
|
||||
user.getCachedData().getPermissionData().checkPermission(parts[1]).asBoolean()
|
||||
);
|
||||
case "primaryGroup" -> user.getPrimaryGroup();
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private record CacheValue(long timestamp, User user){
|
||||
public boolean expired(){
|
||||
// Expired if current timestamp minus old one is more than 60k milliseconds (1 minute)
|
||||
return (timestamp < 0L) || System.currentTimeMillis() - timestamp >= 60_000;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package ch.andre601.advancedserverlist.luckperms.velocity;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.AdvancedServerListAPI;
|
||||
import ch.andre601.advancedserverlist.api.exceptions.InvalidPlaceholderProviderException;
|
||||
import ch.andre601.advancedserverlist.luckperms.placeholders.LuckPermsPlaceholder;
|
||||
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 net.luckperms.api.LuckPerms;
|
||||
import net.luckperms.api.LuckPermsProvider;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class LuckPermsAddon{
|
||||
|
||||
private final ProxyServer proxy;
|
||||
private final Logger logger = LoggerFactory.getLogger("ASLLuckPermsAddon");
|
||||
|
||||
@Inject
|
||||
public LuckPermsAddon(ProxyServer proxy){
|
||||
this.proxy = proxy;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void init(ProxyInitializeEvent event){
|
||||
if(!proxy.getPluginManager().isLoaded("advancedserverlist")){
|
||||
logger.warn("AdvancedServerList is not enabled. This plugin is required.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!proxy.getPluginManager().isLoaded("luckperms")){
|
||||
logger.warn("LuckPerms is not enabled. This plugin is required.");
|
||||
return;
|
||||
}
|
||||
|
||||
LuckPerms luckPerms;
|
||||
try{
|
||||
luckPerms = LuckPermsProvider.get();
|
||||
}catch(IllegalStateException ex){
|
||||
logger.warn("Unable to obtain LuckPerms API instance. The plugin won't work.");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("Registering LuckPerms Placeholder...");
|
||||
try{
|
||||
AdvancedServerListAPI.get().addPlaceholderProvider(new LuckPermsPlaceholder(luckPerms));
|
||||
logger.info("Successfully registered Placeholder!");
|
||||
}catch(InvalidPlaceholderProviderException ex){
|
||||
logger.warn("Unable to register LuckPerms Placeholder! The plugin won't work.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
name: 'ASLLuckPermsAddon'
|
||||
author: 'Andre_601'
|
||||
version: '${plugin.version}'
|
||||
|
||||
main: 'ch.andre601.advancedserverlist.luckperms.paper.LuckPermsAddon'
|
||||
|
||||
api-version: '1.19'
|
||||
|
||||
dependencies:
|
||||
server:
|
||||
AdvancedServerList:
|
||||
required: false
|
||||
load: BEFORE
|
||||
LuckPerms:
|
||||
required: false
|
||||
load: BEFORE
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"id": "aslluckpermsaddon",
|
||||
"name": "ASLLuckPermsAddon",
|
||||
"version": "${plugin.version}",
|
||||
"description": "LuckPerms Add-on for AdvancedServerList to add placeholders to use.",
|
||||
"authors": [
|
||||
"Andre_601"
|
||||
],
|
||||
"dependencies": [
|
||||
{
|
||||
"id": "advancedserverlist",
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"id": "luckperms",
|
||||
"optional": true
|
||||
}
|
||||
],
|
||||
"main": "ch.andre601.advancedserverlist.luckperms.velocity.LuckPermsAddon"
|
||||
}
|
||||
@@ -1,185 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
~
|
||||
-->
|
||||
|
||||
<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>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>bungeecord</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>${plugin.version}</version>
|
||||
<description>${plugin.description}</description>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>16</maven.compiler.source>
|
||||
<maven.compiler.target>16</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>sonatype</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>ch.andre601.advancedserverlist</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>${plugin.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.andre601.asl-api</groupId>
|
||||
<artifactId>platform-bungeecord</artifactId>
|
||||
<version>${api.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- BungeeCord API -->
|
||||
<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>
|
||||
|
||||
<!-- Adventure Library. Needs to be shaded in -->
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-api</artifactId>
|
||||
<version>4.16.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-text-minimessage</artifactId>
|
||||
<version>4.16.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-platform-bungeecord</artifactId>
|
||||
<version>4.3.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Metrics -->
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bungeecord</artifactId>
|
||||
<version>3.0.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Configurate -->
|
||||
<dependency>
|
||||
<groupId>org.spongepowered</groupId>
|
||||
<artifactId>configurate-yaml</artifactId>
|
||||
<version>4.1.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Plugins -->
|
||||
<dependency>
|
||||
<groupId>com.github.LeonMangler</groupId>
|
||||
<artifactId>PremiumVanishAPI</artifactId>
|
||||
<version>2.9.0-4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>AdvancedServerList-BungeeCord-${plugin.version}</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<filtering>true</filtering>
|
||||
<directory>${project.basedir}/src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.12.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.5.2</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>
|
||||
</relocations>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -1,152 +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.bungeecord;
|
||||
|
||||
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.placeholders.BungeePlayerPlaceholders;
|
||||
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 de.myzelyam.api.vanish.BungeeVanishAPI;
|
||||
import net.md_5.bungee.api.Favicon;
|
||||
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;
|
||||
|
||||
public class BungeeCordCore extends Plugin implements PluginCore<Favicon>{
|
||||
|
||||
private AdvancedServerList<Favicon> core;
|
||||
private FaviconHandler<Favicon> faviconHandler = null;
|
||||
private final PluginLogger logger = new BungeeLogger(this);
|
||||
|
||||
@Override
|
||||
public void onEnable(){
|
||||
this.core = AdvancedServerList.init(this, BungeePlayerPlaceholders.init(), BungeeServerPlaceholders.init(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable(){
|
||||
core.disable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadCommands(){
|
||||
getProxy().getPluginManager().registerCommand(this, new CmdAdvancedServerList(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadEvents(){
|
||||
new JoinEvent(this);
|
||||
new PingEvent(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMetrics(){
|
||||
new Metrics(this, 15585).addCustomChart(new SimplePie("profiles",
|
||||
() -> String.valueOf(core.getFileHandler().getProfiles().size())
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFaviconHandler(AdvancedServerList<Favicon> core){
|
||||
faviconHandler = new FaviconHandler<>(core);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearFaviconCache(){
|
||||
if(faviconHandler == null)
|
||||
return;
|
||||
|
||||
faviconHandler.cleanCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdvancedServerList<Favicon> getCore(){
|
||||
return core;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getFolderPath(){
|
||||
return getDataFolder().toPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginLogger getPluginLogger(){
|
||||
return logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaviconHandler<Favicon> getFaviconHandler(){
|
||||
if(faviconHandler == null)
|
||||
faviconHandler = new FaviconHandler<>(core);
|
||||
|
||||
return faviconHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlatformName(){
|
||||
return getProxy().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlatformVersion(){
|
||||
return getProxy().getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLoader(){
|
||||
return "bungeecord";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Favicon createFavicon(BufferedImage image) throws IllegalArgumentException{
|
||||
return Favicon.create(image);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -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.bungeecord.commands;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
|
||||
import net.kyori.adventure.platform.bungeecord.BungeeAudiences;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
|
||||
public class BungeeCmdSender implements CmdSender{
|
||||
|
||||
private final CommandSender sender;
|
||||
private final BungeeAudiences bungeeAudiences;
|
||||
|
||||
public BungeeCmdSender(CommandSender sender, BungeeAudiences bungeeAudiences){
|
||||
this.sender = sender;
|
||||
this.bungeeAudiences = bungeeAudiences;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission){
|
||||
return sender.hasPermission(permission) || sender.hasPermission("advancedserverlist.admin");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMsg(String msg, Object... args){
|
||||
bungeeAudiences.sender(sender).sendMessage(ComponentParser.text(String.format(msg, args)).toComponent());
|
||||
}
|
||||
}
|
||||
@@ -1,48 +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.bungeecord.commands;
|
||||
|
||||
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
|
||||
import net.kyori.adventure.platform.bungeecord.BungeeAudiences;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,207 +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.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.impl.BungeePlayerImpl;
|
||||
import ch.andre601.advancedserverlist.bungeecord.objects.impl.BungeeProxyImpl;
|
||||
import ch.andre601.advancedserverlist.core.events.PingEventHandler;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
|
||||
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 net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||
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.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class BungeeEventWrapper implements GenericEventWrapper<Favicon, BungeePlayerImpl>{
|
||||
|
||||
private final BungeeCordCore plugin;
|
||||
private final ProxyPingEvent event;
|
||||
private final ServerPing.Protocol protocol;
|
||||
private final ServerPing ping;
|
||||
|
||||
public BungeeEventWrapper(BungeeCordCore plugin, ProxyPingEvent event){
|
||||
this.plugin = plugin;
|
||||
this.event = event;
|
||||
this.protocol = event.getResponse().getVersion();
|
||||
this.ping = event.getResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericServerListEvent callEvent(ProfileEntry entry){
|
||||
PreServerListSetEventImpl event = new PreServerListSetEventImpl(entry);
|
||||
plugin.getProxy().getPluginManager().callEvent(event);
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxPlayers(int maxPlayers){
|
||||
ping.getPlayers().setMax(maxPlayers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMotd(Component component){
|
||||
ping.setDescriptionComponent(new TextComponent(BungeeComponentSerializer.get().serialize(component)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hidePlayers(){
|
||||
ping.setPlayers(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerCount(String name){
|
||||
protocol.setName(name);
|
||||
protocol.setProtocol(-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayers(List<String> lines, BungeePlayerImpl player, GenericServer server){
|
||||
ServerPing.PlayerInfo[] players = new ServerPing.PlayerInfo[lines.size()];
|
||||
|
||||
for(int i = 0; i < players.length; i++){
|
||||
String parsed = ComponentParser.text(lines.get(i))
|
||||
.modifyText(text -> StringReplacer.replace(text, player, server))
|
||||
.toString();
|
||||
|
||||
ServerPing.PlayerInfo pi = new ServerPing.PlayerInfo(parsed, UUID.randomUUID());
|
||||
players[i] = pi;
|
||||
}
|
||||
|
||||
if(players.length > 0)
|
||||
ping.getPlayers().setSample(players);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFavicon(Favicon favicon){
|
||||
ping.setFavicon(favicon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultFavicon(){
|
||||
ping.setFavicon(ping.getFaviconObject());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateEvent(){
|
||||
this.ping.setVersion(this.protocol);
|
||||
this.event.setResponse(this.ping);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInvalidProtocol(){
|
||||
return protocol == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMaintenanceModeActive(){
|
||||
if(plugin.getProxy().getPluginManager().getPlugin("Maintenance") == null)
|
||||
return false;
|
||||
|
||||
return PingEventHandler.getMaintenanceUtil().isMaintenanceEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion(){
|
||||
return this.protocol.getProtocol();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOnlinePlayers(){
|
||||
return plugin.getOnlinePlayers(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxPlayers(){
|
||||
return ping.getPlayers().getMax();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlayerIP(){
|
||||
return ((InetSocketAddress)event.getConnection().getSocketAddress()).getHostString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parsePAPIPlaceholders(String text, BungeePlayerImpl player){
|
||||
if(plugin.getProxy().getPluginManager().getPlugin("PAPIProxyBridge") == null)
|
||||
return text;
|
||||
|
||||
if(!PingEventHandler.getPAPIUtil().isCompatible())
|
||||
return text;
|
||||
|
||||
String server = PingEventHandler.getPAPIUtil().getServer();
|
||||
if(server == null || server.isEmpty())
|
||||
return text;
|
||||
|
||||
ServerInfo serverInfo = plugin.getProxy().getServerInfo(server);
|
||||
if(serverInfo == null || serverInfo.getPlayers().isEmpty())
|
||||
return text;
|
||||
|
||||
ProxiedPlayer carrier = PingEventHandler.getPAPIUtil().getPlayer(serverInfo.getPlayers());
|
||||
if(carrier == null)
|
||||
return text;
|
||||
|
||||
return PingEventHandler.getPAPIUtil().parse(text, carrier.getUniqueId(), player.getUUID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVirtualHost(){
|
||||
return this.resolveHost(event.getConnection().getVirtualHost());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginCore<Favicon> getPlugin(){
|
||||
return plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BungeePlayerImpl createPlayer(CachedPlayer player, int protocol){
|
||||
return new BungeePlayerImpl(player, protocol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericServer createGenericServer(int playersOnline, int playersMax, String host){
|
||||
Map<String, ServerInfo> servers = plugin.getProxy().getServers();
|
||||
|
||||
return new BungeeProxyImpl(servers, playersOnline, playersMax, host);
|
||||
}
|
||||
}
|
||||
@@ -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.bungeecord.listeners;
|
||||
|
||||
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.PostLoginEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
public class JoinEvent implements Listener{
|
||||
|
||||
private final BungeeCordCore plugin;
|
||||
|
||||
public JoinEvent(BungeeCordCore plugin){
|
||||
this.plugin = plugin;
|
||||
plugin.getProxy().getPluginManager().registerListener(plugin, this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onJoin(PostLoginEvent event){
|
||||
InetSocketAddress address = (InetSocketAddress)event.getPlayer().getPendingConnection().getSocketAddress();
|
||||
ProxiedPlayer player = event.getPlayer();
|
||||
|
||||
plugin.getCore().getPlayerHandler().addPlayer(address.getHostString(), player.getName(), player.getUniqueId());
|
||||
}
|
||||
}
|
||||
@@ -1,48 +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.bungeecord.listeners;
|
||||
|
||||
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{
|
||||
|
||||
private final BungeeCordCore plugin;
|
||||
|
||||
public PingEvent(BungeeCordCore plugin){
|
||||
this.plugin = plugin;
|
||||
plugin.getProxy().getPluginManager().registerListener(plugin, this);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onProxyPing(ProxyPingEvent event){
|
||||
PingEventHandler.handleEvent(new BungeeEventWrapper(plugin, event));
|
||||
}
|
||||
}
|
||||
@@ -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.bungeecord.logging;
|
||||
|
||||
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class BungeeLogger implements PluginLogger{
|
||||
|
||||
private final BungeeCordCore plugin;
|
||||
private final 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
|
||||
public void info(String msg, Object... args){
|
||||
logger.info(String.format(msg, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String msg, Object... args){
|
||||
logger.warning(String.format(msg, args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String msg, Throwable throwable){
|
||||
logger.log(Level.WARNING, msg, throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String msg, Throwable throwable, Object... args){
|
||||
logger.log(Level.WARNING, String.format(msg, args), throwable);
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* 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.bungeecord.objects.impl;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.bungeecord.objects.BungeeProxy;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public record BungeeProxyImpl(Map<String, ServerInfo> servers, int playersOnline, int playersMax, String host) implements BungeeProxy{
|
||||
@Override
|
||||
public Map<String, ServerInfo> getServers(){
|
||||
return servers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayersOnline(){
|
||||
return playersOnline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayersMax(){
|
||||
return playersMax;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHost(){
|
||||
return host;
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* 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.bungeecord.objects.placeholders;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class BungeePlayerPlaceholders extends PlaceholderProvider{
|
||||
|
||||
private BungeePlayerPlaceholders(){
|
||||
super("player");
|
||||
}
|
||||
|
||||
public static BungeePlayerPlaceholders init(){
|
||||
return new BungeePlayerPlaceholders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
|
||||
return switch(placeholder.toLowerCase(Locale.ROOT)){
|
||||
case "name" -> player.getName();
|
||||
case "protocol" -> String.valueOf(player.getProtocol());
|
||||
case "uuid" -> String.valueOf(player.getUUID());
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* 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.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 final BungeeCordCore plugin;
|
||||
|
||||
private BungeeServerPlaceholders(BungeeCordCore plugin){
|
||||
super("server");
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public static BungeeServerPlaceholders init(BungeeCordCore plugin){
|
||||
return new BungeeServerPlaceholders(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
|
||||
if(!(server instanceof BungeeProxy proxy))
|
||||
return null;
|
||||
|
||||
String[] args = placeholder.split("\\s", 2);
|
||||
|
||||
return switch(args[0]){
|
||||
case "playersOnline" -> {
|
||||
if(args.length >= 2){
|
||||
String[] servers = args[1].split(",");
|
||||
|
||||
int players = 0;
|
||||
for(String serverName : servers){
|
||||
if(serverName.isEmpty())
|
||||
continue;
|
||||
|
||||
ServerInfo info = proxy.getServers().get(serverName.strip());
|
||||
if(info == null)
|
||||
continue;
|
||||
|
||||
players += plugin.getOnlinePlayers(info);
|
||||
}
|
||||
|
||||
yield String.valueOf(players);
|
||||
}
|
||||
|
||||
yield String.valueOf(proxy.getPlayersOnline());
|
||||
}
|
||||
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 String.valueOf(proxy.getPlayersMax());
|
||||
}
|
||||
case "host" -> {
|
||||
if(args.length > 2)
|
||||
yield null;
|
||||
|
||||
if(args.length == 2){
|
||||
ServerInfo info = proxy.getServers().get(args[1]);
|
||||
if(info == null)
|
||||
yield null;
|
||||
|
||||
yield String.valueOf(((InetSocketAddress)info.getSocketAddress()).getHostString());
|
||||
}
|
||||
|
||||
yield proxy.getHost();
|
||||
}
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
name: 'AdvancedServerList'
|
||||
author: 'Andre_601'
|
||||
version: '${plugin.version}'
|
||||
description: '${plugin.description}'
|
||||
|
||||
softDepends:
|
||||
- PAPIProxyBridge
|
||||
- Maintenance
|
||||
|
||||
main: 'ch.andre601.advancedserverlist.bungeecord.BungeeCordCore'
|
||||
@@ -39,9 +39,11 @@
|
||||
<version>${plugin.version}</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>16</maven.compiler.source>
|
||||
<maven.compiler.target>16</maven.compiler.target>
|
||||
<plugin.config.version>4</plugin.config.version>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
<plugin.config.version>10</plugin.config.version>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
@@ -61,6 +63,10 @@
|
||||
<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>
|
||||
@@ -73,13 +79,21 @@
|
||||
<dependency>
|
||||
<groupId>ch.andre601</groupId>
|
||||
<artifactId>expressionparser</artifactId>
|
||||
<version>1.3.0</version>
|
||||
<version>1.7.0-b1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Configuration -->
|
||||
<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.2.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -87,19 +101,19 @@
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-api</artifactId>
|
||||
<version>4.16.0</version>
|
||||
<version>${dependency.adventure-api}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-text-minimessage</artifactId>
|
||||
<version>4.16.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.16.0</version>
|
||||
<version>${dependency.adventure-text-serializer-legacy}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -107,7 +121,7 @@
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.10.1</version>
|
||||
<version>2.15.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -119,21 +133,60 @@
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>33.0.0-jre</version>
|
||||
<version>33.8.0-jre</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Command Framework -->
|
||||
<dependency>
|
||||
<groupId>org.incendo</groupId>
|
||||
<artifactId>cloud-annotations</artifactId>
|
||||
<version>${dependency.cloud-annotations}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.incendo</groupId>
|
||||
<artifactId>cloud-minecraft-extras</artifactId>
|
||||
<version>${dependency.cloud-minecraft-extras}</version>
|
||||
<scope>compile</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Annotations -->
|
||||
<dependency>
|
||||
<groupId>org.jspecify</groupId>
|
||||
<artifactId>jspecify</artifactId>
|
||||
<version>1.0.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Plugins -->
|
||||
<dependency>
|
||||
<groupId>net.william278</groupId>
|
||||
<artifactId>papiproxybridge</artifactId>
|
||||
<version>1.5</version>
|
||||
<version>1.8.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>eu.kennytv.maintenance</groupId>
|
||||
<artifactId>maintenance-api</artifactId>
|
||||
<version>4.2.0-SNAPSHOT</version>
|
||||
<version>5.0.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.minecrell</groupId>
|
||||
<artifactId>ServerListPlus</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.miniplaceholders</groupId>
|
||||
<artifactId>miniplaceholders-api</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -141,59 +194,39 @@
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.10.2</version>
|
||||
<version>5.14.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>AdvancedServerList-Core-${plugin.version}</finalName>
|
||||
<finalName>AdvancedServerList-Core-${plugin.version}${project.build.build_id}</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<filtering>true</filtering>
|
||||
<directory>${project.basedir}/src/main/resources</directory>
|
||||
<excludes>
|
||||
<exclude>char-widths.json.gz</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
<resource>
|
||||
<filtering>false</filtering>
|
||||
<directory>${project.basedir}/src/main/resources</directory>
|
||||
<includes>
|
||||
<include>char-widths.json.gz</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.12.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.5.2</version>
|
||||
<version>3.16.0</version>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<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>
|
||||
<compilerArgs>
|
||||
<arg>-parameters</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
@@ -28,26 +28,58 @@ package ch.andre601.advancedserverlist.core;
|
||||
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.commands.*;
|
||||
import ch.andre601.advancedserverlist.core.compat.maintenance.MaintenancePlaceholder;
|
||||
import ch.andre601.advancedserverlist.core.file.FileHandler;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.ProfileConditionParser;
|
||||
import ch.andre601.advancedserverlist.core.profiles.players.PlayerHandler;
|
||||
import ch.andre601.advancedserverlist.core.parsing.TextCenterUtil;
|
||||
import ch.andre601.advancedserverlist.core.placeholders.DatePlaceholder;
|
||||
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.reader.PlaceholderTokenReader;
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.reader.PlaceholderValueReader;
|
||||
import ch.andre601.advancedserverlist.core.profiles.handlers.PlayerHandler;
|
||||
import ch.andre601.expressionparser.DefaultExpressionParserEngine;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.annotations.AnnotationParser;
|
||||
import org.incendo.cloud.description.Description;
|
||||
import org.incendo.cloud.parser.ParserDescriptor;
|
||||
import org.incendo.cloud.parser.standard.BooleanParser;
|
||||
import org.incendo.cloud.parser.standard.IntegerParser;
|
||||
import org.incendo.cloud.parser.standard.StringParser;
|
||||
import org.incendo.cloud.permission.Permission;
|
||||
import org.incendo.cloud.suggestion.Suggestion;
|
||||
import org.incendo.cloud.suggestion.SuggestionProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
public class AdvancedServerList<F>{
|
||||
|
||||
public static <F> AdvancedServerList<F> init(PluginCore<F> plugin, PlaceholderProvider... placeholders){
|
||||
List<PlaceholderProvider> providers = new ArrayList<>(Arrays.asList(placeholders));
|
||||
providers.add(new DatePlaceholder());
|
||||
|
||||
return new AdvancedServerList<>(plugin, providers);
|
||||
}
|
||||
|
||||
private final PluginCore<F> plugin;
|
||||
private final FileHandler fileHandler;
|
||||
private final CommandHandler commandHandler;
|
||||
private final PlayerHandler playerHandler;
|
||||
private final TextCenterUtil textCenterUtil;
|
||||
|
||||
private static final AdvancedServerListAPI api = AdvancedServerListAPI.get();
|
||||
private final ProfileConditionParser parser = ProfileConditionParser.create();
|
||||
private final DefaultExpressionParserEngine parser = new DefaultExpressionParserEngine.DefaultBuilder()
|
||||
.addDefaults()
|
||||
.addTokenReader(new PlaceholderTokenReader())
|
||||
.addValueReader(new PlaceholderValueReader())
|
||||
.build();
|
||||
|
||||
private UpdateChecker updateChecker;
|
||||
|
||||
@@ -56,56 +88,64 @@ public class AdvancedServerList<F>{
|
||||
private AdvancedServerList(PluginCore<F> plugin, List<PlaceholderProvider> placeholders){
|
||||
this.plugin = plugin;
|
||||
this.fileHandler = new FileHandler(this);
|
||||
this.commandHandler = new CommandHandler(this);
|
||||
this.playerHandler = new PlayerHandler(this);
|
||||
this.textCenterUtil = new TextCenterUtil(this);
|
||||
|
||||
placeholders.forEach(AdvancedServerList.getApi()::addPlaceholderProvider);
|
||||
plugin.pluginLogger().info("Registering internal Placeholders...");
|
||||
|
||||
placeholders.forEach(this::addPlaceholder);
|
||||
|
||||
if(plugin.isPluginEnabled("Maintenance")){
|
||||
addPlaceholder(new MaintenancePlaceholder(this));
|
||||
}
|
||||
|
||||
resolveVersion();
|
||||
plugin().pluginLogger().info("Starting <white>AdvancedServerList v%s</white>...", version);
|
||||
load();
|
||||
}
|
||||
|
||||
public static <F> AdvancedServerList<F> init(PluginCore<F> plugin, PlaceholderProvider... placeholders){
|
||||
return new AdvancedServerList<>(plugin, Arrays.asList(placeholders));
|
||||
}
|
||||
|
||||
public PluginCore<F> getPlugin(){
|
||||
return plugin;
|
||||
}
|
||||
|
||||
public FileHandler getFileHandler(){
|
||||
return fileHandler;
|
||||
}
|
||||
|
||||
public CommandHandler getCommandHandler(){
|
||||
return commandHandler;
|
||||
}
|
||||
|
||||
public PlayerHandler getPlayerHandler(){
|
||||
return playerHandler;
|
||||
}
|
||||
|
||||
public String getVersion(){
|
||||
return version;
|
||||
}
|
||||
|
||||
public static AdvancedServerListAPI getApi(){
|
||||
public static AdvancedServerListAPI api(){
|
||||
return api;
|
||||
}
|
||||
|
||||
public ProfileConditionParser getParser(){
|
||||
public PluginCore<F> plugin(){
|
||||
return plugin;
|
||||
}
|
||||
|
||||
public FileHandler fileHandler(){
|
||||
return fileHandler;
|
||||
}
|
||||
|
||||
public PlayerHandler playerHandler(){
|
||||
return playerHandler;
|
||||
}
|
||||
|
||||
public String version(){
|
||||
return version;
|
||||
}
|
||||
|
||||
public DefaultExpressionParserEngine parser(){
|
||||
return parser;
|
||||
}
|
||||
|
||||
public UpdateChecker updateChecker(){
|
||||
return updateChecker;
|
||||
}
|
||||
|
||||
public TextCenterUtil textCenterUtil(){
|
||||
return textCenterUtil;
|
||||
}
|
||||
|
||||
public void disable(){
|
||||
getPlugin().getPluginLogger().info("Saving playercache.json file...");
|
||||
getPlayerHandler().save();
|
||||
plugin().pluginLogger().info("[-] Saving <white>playercache.json</white> file...");
|
||||
playerHandler().save();
|
||||
|
||||
if(updateChecker != null){
|
||||
getPlugin().getPluginLogger().info("Disabling Update Checker...");
|
||||
plugin().pluginLogger().info("[-] Disabling Update Checker...");
|
||||
updateChecker.disable();
|
||||
}
|
||||
|
||||
getPlugin().getPluginLogger().info("AdvancedServerList disabled!");
|
||||
plugin().pluginLogger().success("AdvancedServerList disabled!");
|
||||
}
|
||||
|
||||
public void clearFaviconCache(){
|
||||
@@ -113,82 +153,195 @@ public class AdvancedServerList<F>{
|
||||
}
|
||||
|
||||
public void clearPlayerCache(){
|
||||
getPlayerHandler().clearCache();
|
||||
playerHandler().clearCache();
|
||||
}
|
||||
|
||||
private void load(){
|
||||
printBanner();
|
||||
resolveVersion();
|
||||
|
||||
getPlugin().getPluginLogger().info("Starting AdvancedServerList v%s...", version);
|
||||
getPlugin().getPluginLogger().info("Platform: " + plugin.getPlatformName() + " " + plugin.getPlatformVersion());
|
||||
|
||||
if(getFileHandler().loadConfig()){
|
||||
getPlugin().getPluginLogger().info("Successfully loaded config.yml!");
|
||||
}else{
|
||||
getPlugin().getPluginLogger().warn("Unable to load config.yml! Check previous lines for errors.");
|
||||
if(!fileHandler().loadConfig()){
|
||||
plugin().pluginLogger().failure("Unable to load <white>config.yml</white>! Check previous lines for errors.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(getFileHandler().isOldConfig()){
|
||||
getPlugin().getPluginLogger().info("Detected old config.yml. Attempting to migrate...");
|
||||
if(getFileHandler().migrateConfig()){
|
||||
getPlugin().getPluginLogger().info("Migration completed successfully!");
|
||||
if(fileHandler().isOldConfig()){
|
||||
plugin().pluginLogger().info("[-] Detected old <white>config.yml</white>. Attempting to migrate...");
|
||||
if(fileHandler().migrateConfig()){
|
||||
plugin().pluginLogger().success("Migration completed successfully!");
|
||||
}else{
|
||||
getPlugin().getPluginLogger().warn("Couldn't migrate config.yml! Check previous lines for errors.");
|
||||
plugin().pluginLogger().failure("Couldn't migrate <white>config.yml</white>! Check previous lines for errors.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(getFileHandler().loadProfiles()){
|
||||
getPlugin().getPluginLogger().info("Successfully loaded %d profile(s)!", getFileHandler().getProfiles().size());
|
||||
if(fileHandler().getBool(true, "printBanner")){
|
||||
printBanner();
|
||||
}
|
||||
|
||||
plugin().pluginLogger().info("Platform: <white>%s</white>", plugin.platformInfo());
|
||||
plugin().pluginLogger().info("");
|
||||
|
||||
if(fileHandler().loadProfiles()){
|
||||
plugin().pluginLogger().success("Successfully loaded <white>%d</white> profile(s)!", fileHandler().profiles().size());
|
||||
}else{
|
||||
getPlugin().getPluginLogger().warn("Unable to load profiles! Check previous lines for errors.");
|
||||
plugin().pluginLogger().failure("Unable to load profiles! Check previous lines for errors.");
|
||||
return;
|
||||
}
|
||||
|
||||
getPlugin().loadFaviconHandler(this);
|
||||
|
||||
getPlugin().getPluginLogger().info("Loading Commands...");
|
||||
plugin.loadCommands();
|
||||
getPlugin().getPluginLogger().info("Commands loaded!");
|
||||
|
||||
getPlugin().getPluginLogger().info("Loading events...");
|
||||
plugin.loadEvents();
|
||||
getPlugin().getPluginLogger().info("Events loaded!");
|
||||
|
||||
getPlugin().getPluginLogger().info("Loading playercache.json...");
|
||||
getPlayerHandler().load();
|
||||
|
||||
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("AdvancedServerList is ready!");
|
||||
loadCommands();
|
||||
|
||||
if(getFileHandler().getBoolean("checkUpdates"))
|
||||
plugin.loadEvents();
|
||||
playerHandler().load();
|
||||
plugin.loadMetrics();
|
||||
plugin.startScheduler();
|
||||
|
||||
plugin().pluginLogger().success("<green>AdvancedServerList is ready!");
|
||||
|
||||
if(fileHandler().getBoolean("checkUpdates"))
|
||||
this.updateChecker = new UpdateChecker(this);
|
||||
}
|
||||
|
||||
private void printBanner(){
|
||||
getPlugin().getPluginLogger().info(" _____ _");
|
||||
getPlugin().getPluginLogger().info(" /\\ / ____| |");
|
||||
getPlugin().getPluginLogger().info(" / \\ | (___ | |");
|
||||
getPlugin().getPluginLogger().info(" / /\\ \\ \\___ \\| |");
|
||||
getPlugin().getPluginLogger().info(" / ____ \\ ____) | |____");
|
||||
getPlugin().getPluginLogger().info("/_/ \\_\\_____/|______|");
|
||||
getPlugin().getPluginLogger().info("");
|
||||
plugin().pluginLogger().info("<#3b90ff> __ _______ ___");
|
||||
plugin().pluginLogger().info("<#3b90ff> /\\ \\ / ____|_| | |");
|
||||
plugin().pluginLogger().info("<#3b90ff> / \\ \\ | (_(___ | | |");
|
||||
plugin().pluginLogger().info("<#3b90ff> / /\\ \\ \\ \\___ \\ \\| | |");
|
||||
plugin().pluginLogger().info("<#3b90ff> / ____ \\ \\ ____) | | |_|____");
|
||||
plugin().pluginLogger().info("<#3b90ff>/_/_/ \\_\\_\\_____/_/|______|_|");
|
||||
plugin().pluginLogger().info("");
|
||||
|
||||
seasonalText();
|
||||
}
|
||||
|
||||
private void resolveVersion(){
|
||||
try(InputStream is = getClass().getResourceAsStream("/version.properties")){
|
||||
try(InputStream stream = getClass().getResourceAsStream("/version.properties")){
|
||||
Properties properties = new Properties();
|
||||
|
||||
properties.load(is);
|
||||
properties.load(stream);
|
||||
|
||||
version = properties.getProperty("version");
|
||||
}catch(IOException ex){
|
||||
version = "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
private void addPlaceholder(PlaceholderProvider placeholderProvider){
|
||||
AdvancedServerList.api().addPlaceholderProvider(placeholderProvider);
|
||||
}
|
||||
|
||||
private void loadCommands(){
|
||||
plugin().loadFaviconHandler(this);
|
||||
|
||||
CommandManager<CmdSender> commandManager = plugin().commandManager();
|
||||
|
||||
commandManager.registerCommandPostProcessor(new CommandTypePostprocessor());
|
||||
|
||||
AnnotationParser<CmdSender> annotationParser = new AnnotationParser<>(commandManager, CmdSender.class);
|
||||
annotationParser.registerBuilderModifier(
|
||||
CommandType.class,
|
||||
(annotation, builder) -> builder.meta(CommandHandler.COMMAND_TYPE, annotation.value())
|
||||
);
|
||||
annotationParser.parse(
|
||||
CmdClearCache.create(this),
|
||||
CmdHelp.create(commandManager),
|
||||
CmdMigrate.create(this),
|
||||
CmdProfiles.create(this),
|
||||
CmdReload.create(this)
|
||||
);
|
||||
|
||||
for(String option : CmdProfiles.ProfileOption.list()){
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("advancedserverlist", "asl")
|
||||
.literal("profiles")
|
||||
.literal("set")
|
||||
.required(
|
||||
"profile",
|
||||
StringParser.stringParser(),
|
||||
Description.of("The profile to edit."),
|
||||
SuggestionProvider.suggesting(
|
||||
fileHandler().profiles().stream()
|
||||
.map(ServerListProfile::file)
|
||||
.map(Suggestion::suggestion)
|
||||
.toList()
|
||||
)
|
||||
)
|
||||
.literal(option)
|
||||
.optional("value", descriptor(option), description(option))
|
||||
.commandDescription(Description.of("Sets or resets profile values."))
|
||||
.handler(context -> CommandHandler.handleSet(this, context))
|
||||
.meta(CommandHandler.OPTION, option)
|
||||
.permission(
|
||||
Permission.anyOf(
|
||||
Permission.permission("advancedserverlist.admin"),
|
||||
Permission.permission("advancedserverlist.command.profiles"),
|
||||
Permission.permission("advancedserverlist.command.profiles.set")
|
||||
)
|
||||
)
|
||||
.meta(CommandHandler.COMMAND_TYPE, CommandType.Type.ALL)
|
||||
);
|
||||
}
|
||||
|
||||
plugin().pluginLogger().success("Loaded Command <white>/advancedserverlist</white>!");
|
||||
}
|
||||
|
||||
private ParserDescriptor<CmdSender, ?> descriptor(String option){
|
||||
return switch(option){
|
||||
case "playercount.hideplayers", "playercount.hideplayershover", "playercount.extraplayers.enabled",
|
||||
"playercount.maxplayers.enabled", "playercount.onlineplayers.enabled" -> BooleanParser.booleanParser();
|
||||
case "priority" -> IntegerParser.integerParser();
|
||||
default -> StringParser.greedyStringParser();
|
||||
};
|
||||
}
|
||||
|
||||
private Description description(String option){
|
||||
return switch(option){
|
||||
case "playercount.hideplayers", "playercount.hideplayershover", "playercount.extraplayers.enabled",
|
||||
"playercount.maxplayers.enabled", "playercount.onlineplayers.enabled" -> Description.of(
|
||||
"Whether this option should be enabled or not. Leave empty to unset it."
|
||||
);
|
||||
case "priority" -> Description.of("Priority this Profile should have. Leave empty to reset.");
|
||||
case "motd", "playercount.hover" -> Description.of(
|
||||
"Lines to set. Use \\n or <newline> to set a linebreak. Leave empty to reset."
|
||||
);
|
||||
default -> Description.of("The Value to use for this option. Leave empty to reset.");
|
||||
};
|
||||
}
|
||||
|
||||
// Adding some seasonal messages
|
||||
private void seasonalText(){
|
||||
LocalDate date = LocalDate.now();
|
||||
|
||||
switch(date.getMonth()){
|
||||
case JANUARY -> {
|
||||
if(date.getDayOfMonth() == 1){
|
||||
plugin().pluginLogger().info("Happy new Year!");
|
||||
plugin().pluginLogger().info("");
|
||||
}
|
||||
}
|
||||
case JUNE -> {
|
||||
plugin().pluginLogger().info("Happy Pride Month!");
|
||||
plugin().pluginLogger().info("");
|
||||
}
|
||||
case AUGUST -> {
|
||||
if(date.getDayOfMonth() == 1){
|
||||
plugin().pluginLogger().info("Happy Birthday Switzerland!");
|
||||
plugin().pluginLogger().info("");
|
||||
}
|
||||
}
|
||||
case OCTOBER -> {
|
||||
if(date.getDayOfMonth() == 31){
|
||||
plugin().pluginLogger().info("Happy Halloween!");
|
||||
plugin().pluginLogger().info("");
|
||||
}
|
||||
}
|
||||
case DECEMBER -> {
|
||||
if(date.getDayOfMonth() == 13){
|
||||
plugin().pluginLogger().info("Happy Birthday Andre_601!");
|
||||
plugin().pluginLogger().info("");
|
||||
}else
|
||||
if(date.getDayOfMonth() > 23 && date.getDayOfMonth() < 27){
|
||||
plugin().pluginLogger().info("Merry Christmas!");
|
||||
plugin().pluginLogger().info("");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,13 +25,13 @@
|
||||
|
||||
package ch.andre601.advancedserverlist.core.check;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
public class UpdateCheckThread implements ThreadFactory{
|
||||
@Override
|
||||
public Thread newThread(@NotNull Runnable r){
|
||||
public Thread newThread(@NonNull Runnable r){
|
||||
Thread t = new Thread(r, "AdvancedServerList-UpdateThread");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
|
||||
@@ -27,6 +27,8 @@ 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.ValueCache;
|
||||
import com.google.gson.FieldNamingPolicy;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
@@ -40,6 +42,7 @@ 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.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executors;
|
||||
@@ -48,32 +51,66 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class UpdateChecker{
|
||||
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
// 'https://api.modrinth.com/v2/project/advancedserverlist/version?loaders=["%s"]' URL encoded.
|
||||
private final String url = "https://api.modrinth.com/v2/project/advancedserverlist/version?loaders=%%5B%%22%s%%22%%5D";
|
||||
private final HttpClient client = HttpClient.newHttpClient();
|
||||
|
||||
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());
|
||||
|
||||
private final ValueCache<CompletableFuture<ModrinthVersion>> cache = new ValueCache<>(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.logger = core.plugin().pluginLogger();
|
||||
this.loader = core.plugin().loader();
|
||||
this.client = HttpClient.newBuilder().executor(Executors.newSingleThreadExecutor(new UpdateCheckThread())).build();
|
||||
|
||||
startUpdateChecker();
|
||||
}
|
||||
|
||||
public void performCachedUpdateCheck(CmdSender sender){
|
||||
CompletableFuture<ModrinthVersion> modrinthVersion = cache.get(this::performUpdateCheck);
|
||||
|
||||
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.version());
|
||||
if(result == -1){
|
||||
sender.sendPrefixedMsg("<green>A new Version of AdvancedServerList is available!");
|
||||
sender.sendPrefixedMsg("<green>Your version: <white>%s", core.version());
|
||||
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(
|
||||
"Your version (<white>%</white>) is newer than what is available on Modrinth (<white>%s</white>).",
|
||||
core.version(),
|
||||
version.versionNumber()
|
||||
);
|
||||
sender.sendPrefixedMsg("Are you using a Development Version?");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void disable(){
|
||||
executor.shutdown();
|
||||
try{
|
||||
@@ -90,35 +127,43 @@ public class UpdateChecker{
|
||||
|
||||
private void startUpdateChecker(){
|
||||
executor.scheduleAtFixedRate(() -> {
|
||||
logger.info("Looking for an update...");
|
||||
logger.info("[-] Looking for an update...");
|
||||
|
||||
performUpdateCheck().whenComplete((version, throwable) -> {
|
||||
if(version == null || throwable != null){
|
||||
logger.warn("Failed to look for any updates. Check previous messages for causes.");
|
||||
logger.failure("Failed to look for any updates. Check previous messages for causes.");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug(UpdateChecker.class, "Comparing versions...");
|
||||
|
||||
int result = version.compare(core.getVersion());
|
||||
int result = version.compare(core.version());
|
||||
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()
|
||||
);
|
||||
case 0 -> logger.success("You are running the latest version!");
|
||||
case 1 -> {
|
||||
logger.info(
|
||||
"[?] Your version (<white>%s</white>) is newer than what is available on Modrint (<white>%s</white>).",
|
||||
core.version(),
|
||||
version.versionNumber()
|
||||
);
|
||||
logger.info(" Are you using a Development Version?");
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 0L, 12L, TimeUnit.HOURS);
|
||||
}
|
||||
|
||||
private CompletableFuture<ModrinthVersion> performUpdateCheck(){
|
||||
String finalUrl = String.format(url, loader);
|
||||
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")){
|
||||
if(core.version().equals("UNKNOWN")){
|
||||
logger.warn("Cannot perform update check. Plugin version is 'UNKNOWN'!");
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
@@ -127,7 +172,8 @@ public class UpdateChecker{
|
||||
try{
|
||||
request = HttpRequest.newBuilder()
|
||||
.uri(new URI(finalUrl))
|
||||
.header("User-Agent", "AdvancedServerList-" + loader + "/" + core.getVersion())
|
||||
.timeout(Duration.ofSeconds(5))
|
||||
.header("User-Agent", "AdvancedServerList-" + loader + "/" + core.version())
|
||||
.build();
|
||||
}catch(URISyntaxException ex){
|
||||
logger.warn("Cannot perform update check. URL '%s' is not a valid URI.", ex, finalUrl);
|
||||
@@ -144,22 +190,22 @@ public class UpdateChecker{
|
||||
}
|
||||
|
||||
private void printUpdateBanner(ModrinthVersion version){
|
||||
logger.info("==================================================================");
|
||||
logger.info("You are running an outdated version of AdvancedServerList!");
|
||||
logger.info("");
|
||||
logger.info("Your version: %s", core.getVersion());
|
||||
logger.info("Modrinth: %s", version.versionNumber());
|
||||
logger.info("");
|
||||
logger.warn("=======================================================================");
|
||||
logger.warn("You are running an outdated version of AdvancedServerList!");
|
||||
logger.warn("");
|
||||
logger.warn("Your version: <white>%s</white>", core.version());
|
||||
logger.warn("Modrinth: <white>%s</white>", version.versionNumber());
|
||||
logger.warn("");
|
||||
|
||||
if(!version.isRelease()){
|
||||
logger.info("WARNING!");
|
||||
logger.info("This is a %s version and may contain breaking changes and/or bugs!", version.versionType());
|
||||
logger.info("");
|
||||
logger.warn("<bold>WARNING!</bold>");
|
||||
logger.warn("This is a %s version and may contain breaking changes and/or bugs!", version.versionType());
|
||||
logger.warn("");
|
||||
}
|
||||
|
||||
logger.info("Download the latest version from here:");
|
||||
logger.info("https://modrinth.com/plugin/advancedserverlist/version/%s", version.id());
|
||||
logger.info("==================================================================");
|
||||
logger.warn("Download the latest version from here:");
|
||||
logger.warn("<white>https://modrinth.com/plugin/advancedserverlist/version/%s</white>", version.id());
|
||||
logger.warn("=======================================================================");
|
||||
}
|
||||
|
||||
public record ModrinthVersion(String id, String versionNumber, String versionType){
|
||||
@@ -171,10 +217,5 @@ public class UpdateChecker{
|
||||
public int compare(String version){
|
||||
return FlexVerComparator.compare(version, versionNumber());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return String.format("ModrinthVersion{id=%s,versionNumber=%s,versionType=%s}", id, versionNumber, versionType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package ch.andre601.advancedserverlist.core.commands;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
|
||||
import org.incendo.cloud.annotations.Argument;
|
||||
import org.incendo.cloud.annotations.Command;
|
||||
import org.incendo.cloud.annotations.CommandDescription;
|
||||
import org.incendo.cloud.annotations.Permission;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
|
||||
@Command("advancedserverlist|asl")
|
||||
@CommandDescription("Main Command of the plugin")
|
||||
@Permission("advancedserverlist.admin")
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public record CmdClearCache(AdvancedServerList<?> core){
|
||||
|
||||
public static CmdClearCache create(AdvancedServerList<?> core){
|
||||
return new CmdClearCache(core);
|
||||
}
|
||||
|
||||
@Command("clearcache [target]")
|
||||
@CommandDescription("Cleares the Favicon and/or Player Cache.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.clearcache"})
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public void handle(
|
||||
CmdSender sender,
|
||||
@Nullable @Argument(description = "The type of Cache to clean. Defaults to ALL.") CacheTarget target
|
||||
){
|
||||
if(target == null)
|
||||
target = CacheTarget.ALL;
|
||||
|
||||
sender.sendPrefixedMsg("Clearing cache...");
|
||||
|
||||
if(target == CacheTarget.ALL || target == CacheTarget.FAVICON){
|
||||
core().clearFaviconCache();
|
||||
sender.sendPrefixedMsg("Cleared Favicon Cache.");
|
||||
}
|
||||
|
||||
if(target == CacheTarget.ALL || target == CacheTarget.PLAYER){
|
||||
core().clearPlayerCache();
|
||||
sender.sendPrefixedMsg("Cleared Player Cache.");
|
||||
}
|
||||
|
||||
sender.sendPrefixedMsg("<green>Cache clearing complete!");
|
||||
}
|
||||
|
||||
public enum CacheTarget{
|
||||
ALL,
|
||||
FAVICON,
|
||||
PLAYER
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package ch.andre601.advancedserverlist.core.commands;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.annotation.specifier.Greedy;
|
||||
import org.incendo.cloud.annotations.Argument;
|
||||
import org.incendo.cloud.annotations.Command;
|
||||
import org.incendo.cloud.annotations.CommandDescription;
|
||||
import org.incendo.cloud.annotations.Permission;
|
||||
import org.incendo.cloud.annotations.suggestion.Suggestions;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
import org.incendo.cloud.help.result.CommandEntry;
|
||||
import org.incendo.cloud.minecraft.extras.MinecraftHelp;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Command("advancedserverlist|asl")
|
||||
@CommandDescription("Main Command of the plugin")
|
||||
@Permission("advancedserverlist.admin")
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public record CmdHelp(CommandManager<CmdSender> manager, MinecraftHelp<CmdSender> help){
|
||||
|
||||
public static CmdHelp create(CommandManager<CmdSender> manager){
|
||||
MinecraftHelp<CmdSender> help = MinecraftHelp.<CmdSender>builder()
|
||||
.commandManager(manager)
|
||||
.audienceProvider(CmdSender::audience)
|
||||
.commandPrefix("/asl help")
|
||||
.colors(MinecraftHelp.helpColors(
|
||||
NamedTextColor.WHITE, NamedTextColor.AQUA, NamedTextColor.WHITE, NamedTextColor.GRAY, NamedTextColor.DARK_GRAY
|
||||
))
|
||||
.build();
|
||||
|
||||
return new CmdHelp(manager, help);
|
||||
}
|
||||
|
||||
@Command("help [query]")
|
||||
@CommandDescription("Displays all commands or provides info about a specific one.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.help"})
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public void handle(
|
||||
CmdSender sender,
|
||||
@Nullable @Argument(description = "Search Query for a specific command", suggestions = "help") @Greedy String query
|
||||
){
|
||||
this.help.queryCommands(query == null ? "" : query, sender);
|
||||
}
|
||||
|
||||
@Suggestions("help")
|
||||
public List<String> helpQueries(CommandContext<CmdSender> context) {
|
||||
return this.manager.createHelpHandler()
|
||||
.queryRootIndex(context.sender())
|
||||
.entries()
|
||||
.stream()
|
||||
.filter(entry -> context.hasPermission(entry.command().commandPermission()))
|
||||
.filter(entry -> {
|
||||
CommandType.Type type = entry.command().commandMeta().getOrDefault(CommandHandler.COMMAND_TYPE, CommandType.Type.ALL);
|
||||
|
||||
return type == CommandType.Type.ALL || type == CommandType.Type.PLAYER_ONLY && context.sender().isPlayer();
|
||||
})
|
||||
.map(CommandEntry::syntax)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package ch.andre601.advancedserverlist.core.commands;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
|
||||
import ch.andre601.advancedserverlist.core.migration.minimotd.MiniMOTDConfigMigrator;
|
||||
import ch.andre601.advancedserverlist.core.migration.serverlistplus.SLPConfigMigrator;
|
||||
import org.incendo.cloud.annotations.Argument;
|
||||
import org.incendo.cloud.annotations.Command;
|
||||
import org.incendo.cloud.annotations.CommandDescription;
|
||||
import org.incendo.cloud.annotations.Permission;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@Command("advancedserverlist|asl")
|
||||
@CommandDescription("Main Command of the plugin")
|
||||
@Permission("advancedserverlist.admin")
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public record CmdMigrate(AdvancedServerList<?> core){
|
||||
|
||||
public static CmdMigrate create(AdvancedServerList<?> core){
|
||||
return new CmdMigrate(core);
|
||||
}
|
||||
|
||||
@Command("migrate <plugin>")
|
||||
@CommandDescription("Migrates Profiles from supported Plugins to ASL.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.migrate"})
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public void handle(
|
||||
CmdSender sender,
|
||||
@NonNull @Argument(description = "Name of the plugin to migrate from") MigratablePlugin plugin
|
||||
){
|
||||
if(plugin == MigratablePlugin.SERVERLISTPLUS){
|
||||
if(!core().plugin().isPluginEnabled("ServerListPlus")){
|
||||
sender.sendErrorMsg("<red>ServerListPlus not enabled.");
|
||||
sender.sendErrorMsg("<red>The plugin needs to be enabled for the migration to work.");
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendPrefixedMsg("Migrating from ServerListPlus...");
|
||||
|
||||
int migrated = SLPConfigMigrator.migrate(core(), sender);
|
||||
if(migrated == 0){
|
||||
sender.sendErrorMsg("<red>Couldn't migrate any profiles from ServerListPlus.");
|
||||
sender.sendErrorMsg("<red>See the server console for any errors.");
|
||||
return;
|
||||
}else{
|
||||
sender.sendPrefixedMsg("<green>Successfully migrated </green>%d<green> Profile(s)!", migrated);
|
||||
}
|
||||
}else
|
||||
if(plugin == MigratablePlugin.MINIMOTD){
|
||||
sender.sendPrefixedMsg("Migrating from MiniMOTD...");
|
||||
|
||||
if(MiniMOTDConfigMigrator.migrate(core(), sender)){
|
||||
sender.sendPrefixedMsg("<green>Successfully migrated config from MiniMOTD!");
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Couldn't migrate config from MiniMOTD.");
|
||||
sender.sendErrorMsg("<red>See the server console for any errors.");
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Received invalid Plugin </red>%s<red>!", plugin.toString());
|
||||
sender.sendErrorMsg("<red>Only the following plugins are supported:");
|
||||
|
||||
for(MigratablePlugin pl : MigratablePlugin.values()){
|
||||
sender.sendErrorMsg("- <click:suggest_command:/asl migrate %s>%s</click>", pl.name().toLowerCase(Locale.ROOT), pl.name().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
}
|
||||
|
||||
sender.sendPrefixedMsg("<green>Run <white><click:suggest_command:/asl reload>/asl reload</click></white> to load the new Profile(s).");
|
||||
}
|
||||
|
||||
public enum MigratablePlugin{
|
||||
SERVERLISTPLUS,
|
||||
MINIMOTD
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,483 @@
|
||||
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.CommandType;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.dialogs.IDialog;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.dialogs.IDialogList;
|
||||
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
|
||||
import ch.andre601.advancedserverlist.core.profiles.profile.ProfileSerializer;
|
||||
import org.incendo.cloud.annotations.Argument;
|
||||
import org.incendo.cloud.annotations.Command;
|
||||
import org.incendo.cloud.annotations.CommandDescription;
|
||||
import org.incendo.cloud.annotations.Permission;
|
||||
import org.incendo.cloud.annotations.suggestion.Suggestions;
|
||||
import org.incendo.cloud.context.CommandInput;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
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.*;
|
||||
|
||||
@Command("advancedserverlist|asl")
|
||||
@CommandDescription("Main Command of the plugin.")
|
||||
@Permission("advancedserverlist.admin")
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public record CmdProfiles(AdvancedServerList<?> core){
|
||||
|
||||
public static CmdProfiles create(AdvancedServerList<?> core){
|
||||
return new CmdProfiles(core);
|
||||
}
|
||||
|
||||
@Command("profiles add <name>")
|
||||
@CommandDescription("Creates a new Profile with default values applied.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.profiles", "advancedserverlist.command.profiles.add"})
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public void add(
|
||||
CmdSender sender,
|
||||
@NonNull @Argument(description = "Name of the profile to create.") String name
|
||||
){
|
||||
if(name.isEmpty()){
|
||||
sender.sendErrorMsg("<red>Insufficient arguments.");
|
||||
sender.sendErrorMsg("<red>A profile name needs to be provided.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(CommandHandler.profile(core(), name) != null){
|
||||
sender.sendErrorMsg("<red>A profile with the name </red>%s<red> already exists.", name);
|
||||
return;
|
||||
}
|
||||
|
||||
String file = CommandHandler.profileName(name);
|
||||
|
||||
if(core().fileHandler().createFile(file)){
|
||||
sender.sendPrefixedMsg("<green>Successfully created </green>%s<green>!", file);
|
||||
sender.sendPrefixedMsg("<green>Use <white><click:suggest_command:/asl reload>/asl reload</click></white> to load the file.");
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Error while creating new profile.");
|
||||
sender.sendErrorMsg("<red>Check the server console for details.");
|
||||
}
|
||||
}
|
||||
|
||||
@Command("profiles copy <profile> <name>")
|
||||
public void copy(
|
||||
CmdSender sender,
|
||||
@NonNull @Argument(description = "Name of the profile to copy.", suggestions = "profiles") String profile,
|
||||
@NonNull @Argument(description = "Name of the copy to have.") String name
|
||||
){
|
||||
if(profile.isEmpty() || name.isEmpty()){
|
||||
sender.sendErrorMsg("<red>Insufficient arguments.");
|
||||
sender.sendErrorMsg("<red>A profile name and name for the copy need to be provided.");
|
||||
return;
|
||||
}
|
||||
|
||||
String profileName = CommandHandler.profileName(profile);
|
||||
String copyName = CommandHandler.profileName(name);
|
||||
|
||||
ServerListProfile original = CommandHandler.profile(core(), profileName);
|
||||
|
||||
if(original == null){
|
||||
sender.sendErrorMsg("<red>A profile with name </red>%s<red> does not exist.", profile);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean copyExists = CommandHandler.profile(core(), copyName) != null;
|
||||
if(copyExists){
|
||||
sender.sendErrorMsg("<red>A profile with name </red>%s<red> already exists.", name);
|
||||
return;
|
||||
}
|
||||
|
||||
Path profilePath = core().plugin().folderPath().resolve("profiles").resolve(copyName);
|
||||
if(!Files.exists(profilePath)){
|
||||
try{
|
||||
Files.createFile(profilePath);
|
||||
}catch(IOException ex){
|
||||
sender.sendErrorMsg("<red>Encountered an IOException while creating </red>%s<red>.", copyName);
|
||||
sender.sendErrorMsg("<red>Please see the server console for further details.");
|
||||
|
||||
core.plugin().pluginLogger().warn("Encountered IOException while creating file <white>%s</white>", ex, copyName);
|
||||
return;
|
||||
}
|
||||
|
||||
YamlConfigurationLoader loader = YamlConfigurationLoader.builder()
|
||||
.path(profilePath)
|
||||
.indent(2)
|
||||
.nodeStyle(NodeStyle.BLOCK)
|
||||
.defaultOptions(opt -> opt.serializers(builder -> builder.register(ProfileEntry.class, ProfileSerializer.INSTANCE)))
|
||||
.build();
|
||||
|
||||
ConfigurationNode node;
|
||||
try{
|
||||
node = loader.load();
|
||||
}catch(IOException ex){
|
||||
sender.sendErrorMsg("<red>Encountered an IOException while loading file </red>%s<red>.", copyName);
|
||||
sender.sendErrorMsg("<red>Please see the server console for further details.");
|
||||
|
||||
core().plugin().pluginLogger().warn("Encountered IOException while loading file <white>%s</white>.", ex, copyName);
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
node.node("priority").set(original.priority());
|
||||
node.node("condition").set(original.condition());
|
||||
|
||||
node.node("profiles").setList(ProfileEntry.class, original.profiles());
|
||||
|
||||
node.set(ProfileEntry.class, original.defaultProfile());
|
||||
}catch(SerializationException ex){
|
||||
sender.sendErrorMsg("<red>Encountered a SerializationException while copying values from </red>%s<red> to </red>%s<red>.", profileName, copyName);
|
||||
sender.sendErrorMsg("<red>Please see the server console for further details.");
|
||||
|
||||
core().plugin().pluginLogger().warn("<red>Encountered a SerializationException while copying values from </red>%s<red> to </red>%s<red>.", ex, profileName, copyName);
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
loader.save(node);
|
||||
sender.sendPrefixedMsg("<green>Successfully created copy of </green>%s<green> as </green>%s<green>!", profileName, copyName);
|
||||
sender.sendPrefixedMsg("<green>Use <white><click:suggest_command:/asl reload>/asl reload</click></white> to load the file.");
|
||||
}catch(IOException ex){
|
||||
sender.sendErrorMsg("<red>There was an IOException while loading file </red>%s<red>!", copyName);
|
||||
sender.sendErrorMsg("<red>Please see the server console for further details.");
|
||||
|
||||
core.plugin().pluginLogger().warn("Encountered an IOException while loading file <white>%s</white>", ex, copyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Command("profiles edit <profile>")
|
||||
@CommandDescription("Opens a Dialog to edit a specific profile ingame.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.profiles", "advancedserverlist.command.profiles.edit"})
|
||||
@CommandType(CommandType.Type.PLAYER_ONLY)
|
||||
public void edit(
|
||||
CmdSender sender,
|
||||
@NonNull @Argument(description = "Name of the profile to edit.", suggestions = "profiles") String profile
|
||||
){
|
||||
if(!core().plugin().isDialogsSupported()){
|
||||
sender.sendErrorMsg("Dialogs are not supported on this server or proxy version.");
|
||||
return;
|
||||
}
|
||||
|
||||
ServerListProfile slp = CommandHandler.profile(core(), profile);
|
||||
if(slp == null){
|
||||
sender.sendErrorMsg("<red>A profile with name </red>%s<red> does not exist.", profile);
|
||||
return;
|
||||
}
|
||||
|
||||
IDialogList.IDialogListBuilder listBuilder = core().plugin().createDialogListBuilder(slp.file());
|
||||
String warning = """
|
||||
<gold><bold>WARNING!</bold></gold>
|
||||
|
||||
Dialog-based Profile editing is currently an <italic>experimental feature</italic>!
|
||||
Please create backups of your profiles before using this feature and report any bugs and/or glitches you encounter.""";
|
||||
|
||||
listBuilder.appendBody(warning);
|
||||
|
||||
listBuilder.appendDialog(
|
||||
createDialog("Main Settings", warning, slp, slp.defaultProfile(), slp.priority(), slp.condition(), -1)
|
||||
);
|
||||
|
||||
for(int i = 0; i < slp.profiles().size(); i++){
|
||||
listBuilder.appendDialog(
|
||||
createDialog("Profiles #" + (i + 1), warning, slp, slp.profiles().get(i), -1, null, i)
|
||||
);
|
||||
}
|
||||
|
||||
listBuilder.build().show(sender);
|
||||
}
|
||||
|
||||
@Command("profiles info <profile>")
|
||||
@CommandDescription("Provides info about a specific profile.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.profiles", "advancedserverlist.command.profiles.info"})
|
||||
@CommandType(CommandType.Type.PLAYER_ONLY)
|
||||
public void info(
|
||||
CmdSender sender,
|
||||
@NonNull @Argument(description = "Name of the profile to view.", suggestions = "profiles") String profile
|
||||
){
|
||||
if(profile.isEmpty()){
|
||||
sender.sendErrorMsg("<red>Insufficient arguments.");
|
||||
sender.sendErrorMsg("<red>A profile name needs to be provided.");
|
||||
return;
|
||||
}
|
||||
|
||||
ServerListProfile slp = CommandHandler.profile(core(), profile);
|
||||
|
||||
if(slp == null){
|
||||
sender.sendErrorMsg("<red>Unable to find profile with name </red>%s<red>.", profile);
|
||||
sender.sendErrorMsg("<red>Make sure you typed it correctly.");
|
||||
return;
|
||||
}
|
||||
|
||||
ProfileOptionsSender optionsSender = new ProfileOptionsSender(slp.file());
|
||||
|
||||
optionsSender
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Priority", slp.priority())
|
||||
.option(ProfileOption.PRIORITY)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Condition", slp.condition())
|
||||
.option(ProfileOption.CONDITION)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Profiles")
|
||||
.color(slp.profiles().isEmpty() ? ProfileOptionsSender.ColorName.RED : ProfileOptionsSender.ColorName.GREEN)
|
||||
);
|
||||
|
||||
if(!slp.profiles().isEmpty()){
|
||||
for(int i = 0; i < slp.profiles().size(); i++){
|
||||
optionsSender.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("#" + (i + 1), profileHover(slp.profiles().get(i)))
|
||||
.prefix("│ %s─".formatted((i + 1) == slp.profiles().size() ? "└" : "├"))
|
||||
.color(ProfileOptionsSender.ColorName.AQUA)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ProfileEntry entry = slp.defaultProfile();
|
||||
|
||||
optionsSender
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("MOTD", entry.motd())
|
||||
.option(ProfileOption.MOTD)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Favicon", entry.favicon())
|
||||
.option(ProfileOption.FAVICON)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Player count")
|
||||
.prefix("└─")
|
||||
.color(ProfileOptionsSender.ColorName.AQUA)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Hide Player count?", entry.hidePlayersEnabled())
|
||||
.prefix(" ├─")
|
||||
.option(ProfileOption.PLAYERCOUNT_HIDEPLAYERS)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Hide Player count hover?", entry.hidePlayersHoverEnabled())
|
||||
.prefix(" ├─")
|
||||
.option(ProfileOption.PLAYERCOUNT_HIDEPLAYERSHOVER)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Player count hover", entry.players())
|
||||
.prefix(" ├─")
|
||||
.option(ProfileOption.PLAYERCOUNT_HOVER)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Player count text", entry.playerCountText())
|
||||
.prefix(" ├─")
|
||||
.option(ProfileOption.PLAYERCOUNT_TEXT)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Extra Players")
|
||||
.prefix(" ├─")
|
||||
.color(ProfileOptionsSender.ColorName.AQUA)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Enabled?", entry.extraPlayersEnabled())
|
||||
.prefix(" │ ├─")
|
||||
.option(ProfileOption.PLAYERCOUNT_EXTRAPLAYERS_ENABLED)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Amount", entry.extraPlayersCount())
|
||||
.prefix(" │ └─")
|
||||
.option(ProfileOption.PLAYERCOUNT_EXTRAPLAYERS_AMOUNT)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Max Players")
|
||||
.prefix(" ├─")
|
||||
.color(ProfileOptionsSender.ColorName.AQUA)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Enabled?", entry.maxPlayersEnabled())
|
||||
.prefix(" │ ├─")
|
||||
.option(ProfileOption.PLAYERCOUNT_MAXPLAYERS_ENABLED)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Amount", entry.maxPlayersCount())
|
||||
.prefix(" │ └─")
|
||||
.option(ProfileOption.PLAYERCOUNT_MAXPLAYERS_AMOUNT)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Online Players")
|
||||
.prefix(" └─")
|
||||
.color(ProfileOptionsSender.ColorName.AQUA)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Enabled?", entry.onlinePlayersEnabled())
|
||||
.prefix(" ├─")
|
||||
.option(ProfileOption.PLAYERCOUNT_ONLINEPLAYERS_ENABLED)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Amount", entry.onlinePlayersCount())
|
||||
.prefix(" └─")
|
||||
.option(ProfileOption.PLAYERCOUNT_ONLINEPLAYERS_AMOUNT)
|
||||
).send(sender);
|
||||
}
|
||||
|
||||
@Command("profiles list")
|
||||
@CommandDescription("Lists all available profiles with priority, conditions and if they are valid.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.profiles", "advancedserverlist.command.profiles.list"})
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public void list(CmdSender sender){
|
||||
sender.sendMsg();
|
||||
sender.sendPrefixedMsg("Available Profiles:");
|
||||
sender.sendMsg("<dark_grey>|");
|
||||
|
||||
List<ServerListProfile> profiles = core().fileHandler().profiles().stream()
|
||||
.sorted(Comparator.comparingInt(ServerListProfile::priority).reversed())
|
||||
.toList();
|
||||
|
||||
if(profiles.isEmpty()){
|
||||
sender.sendMsg("<dark_grey></dark_grey> <i>No profiles found.</i>");
|
||||
}else{
|
||||
for(int i = 0; i < profiles.size(); i++){
|
||||
sender.sendMsg(
|
||||
"<dark_grey>%s</dark_grey> <aqua>" +
|
||||
"<hover:show_text:\"%s\">" +
|
||||
"<click:suggest_command:/asl profiles info %s>" +
|
||||
"%s" +
|
||||
"</click>" +
|
||||
"</hover>" +
|
||||
"</aqua>",
|
||||
(i + 1) == profiles.size() ? "" : "-",
|
||||
hover(profiles.get(i)),
|
||||
profiles.get(i).file(),
|
||||
profiles.get(i).file()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
sender.sendMsg();
|
||||
sender.sendMsg("<grey><i>Hover</i></grey> = Show details.");
|
||||
sender.sendMsg("<grey><i>Click</i></grey> = Get Command.");
|
||||
}
|
||||
|
||||
@Suggestions("profiles")
|
||||
public List<String> profilesSuggestions(CommandInput input){
|
||||
String name = input.readString();
|
||||
List<String> profiles = core().fileHandler().profiles().stream()
|
||||
.map(profile -> profile.file().substring(0, profile.file().lastIndexOf('.')))
|
||||
.toList();
|
||||
if(name.isEmpty())
|
||||
return profiles;
|
||||
|
||||
List<String> matches = new ArrayList<>(profiles.size());
|
||||
for(String str : profiles){
|
||||
if(str.length() >= name.length() && str.regionMatches(true, 0, name, 0, name.length()))
|
||||
matches.add(str);
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
private String hover(ServerListProfile profile){
|
||||
return """
|
||||
<grey>Priority: <white>%d</white>
|
||||
Condition: <white>%s</white>
|
||||
|
||||
Valid? %s""".formatted(
|
||||
profile.priority(),
|
||||
profile.condition() == null || profile.condition().isEmpty() ? "<i>None</i>" : profile.condition(),
|
||||
profile.isInvalidProfile() ? "<red>No</red>" : "<green>Yes</green>"
|
||||
);
|
||||
}
|
||||
|
||||
private String profileHover(ProfileEntry entry){
|
||||
return """
|
||||
<grey>MOTD:
|
||||
%s<reset>
|
||||
|
||||
<grey>Favicon: %s<reset>
|
||||
|
||||
<grey>Hide Player count? %s
|
||||
Hide Player count Hover? %s
|
||||
|
||||
Player count Hover:
|
||||
%s<reset>
|
||||
|
||||
<grey>Player count Text: %s<reset>
|
||||
|
||||
<grey>Extra Players: %s<reset>
|
||||
<grey>Max Players: %s<reset>
|
||||
<grey>Online Players: %s""".formatted(
|
||||
entry.motd() == null || entry.motd().isEmpty() ?
|
||||
"<i><white>None</white></i>" : String.join("<reset>\n", entry.motd()),
|
||||
entry.favicon() == null || entry.favicon().isEmpty() ?
|
||||
"<i><white>None</white></i>" : entry.favicon(),
|
||||
entry.hidePlayersEnabled().getOrDefault(false) ? "<green>Enabled</green>" : "<red>Disabled</red>",
|
||||
entry.hidePlayersHoverEnabled().getOrDefault(false) ? "<green>Enabled</green>" : "<red>Disabled</red>",
|
||||
entry.players().isEmpty() ? "<i><white>None</white></i>" : String.join("<reset>\n", entry.players()),
|
||||
entry.playerCountText() == null || entry.playerCountText().isEmpty() ?
|
||||
"<i><white>None</white></i>" : entry.playerCountText(),
|
||||
entry.extraPlayersEnabled().getOrDefault(false) ? entry.extraPlayersCount() : "<red>Disabled</red>",
|
||||
entry.maxPlayersEnabled().getOrDefault(false) ? entry.maxPlayersCount() : "<red>Disabled</red>",
|
||||
entry.onlinePlayersEnabled().getOrDefault(false) ? entry.onlinePlayersCount() : "<red>Disabled</red>"
|
||||
);
|
||||
}
|
||||
|
||||
private IDialog<?> createDialog(
|
||||
String title,
|
||||
String warning,
|
||||
ServerListProfile slp,
|
||||
ProfileEntry entry,
|
||||
int priority,
|
||||
String condition,
|
||||
int index
|
||||
){
|
||||
IDialog.IDialogBuilder builder = core.plugin().createDialogBuilder(title, slp, index);
|
||||
builder.appendBody(warning);
|
||||
|
||||
if(index < 0){
|
||||
builder.appendNumberRange(ProfileOption.PRIORITY, "Priority", priority)
|
||||
.appendTextInput(ProfileOption.CONDITION, "Condition", condition, 100);
|
||||
}
|
||||
|
||||
builder.appendTextbox(ProfileOption.MOTD, "MOTD", entry.motd(), 200, 2)
|
||||
.appendTextInput(ProfileOption.FAVICON, "Favicon", entry.favicon(), 100)
|
||||
.appendBoolean(ProfileOption.PLAYERCOUNT_HIDEPLAYERS, "Hide Player Count", entry.hidePlayersEnabled())
|
||||
.appendBoolean(ProfileOption.PLAYERCOUNT_HIDEPLAYERSHOVER, "Hide Player Count Hover", entry.hidePlayersHoverEnabled())
|
||||
.appendTextbox(ProfileOption.PLAYERCOUNT_HOVER, "Player Count Hover", entry.players(), 200, 10)
|
||||
.appendTextInput(ProfileOption.PLAYERCOUNT_TEXT, "Player Count Text", entry.playerCountText(), 100)
|
||||
.appendBoolean(ProfileOption.PLAYERCOUNT_EXTRAPLAYERS_ENABLED, "Enable Extra Players", entry.extraPlayersEnabled())
|
||||
.appendTextInput(ProfileOption.PLAYERCOUNT_EXTRAPLAYERS_AMOUNT, "Extra Players Count", entry.extraPlayersCount(), 100)
|
||||
.appendBoolean(ProfileOption.PLAYERCOUNT_MAXPLAYERS_ENABLED, "Enable Max Players", entry.maxPlayersEnabled())
|
||||
.appendTextInput(ProfileOption.PLAYERCOUNT_MAXPLAYERS_AMOUNT, "Max Players Count", entry.maxPlayersCount(), 100)
|
||||
.appendBoolean(ProfileOption.PLAYERCOUNT_ONLINEPLAYERS_ENABLED, "Enable Online Players", entry.onlinePlayersEnabled())
|
||||
.appendTextInput(ProfileOption.PLAYERCOUNT_ONLINEPLAYERS_AMOUNT, "Online Players Count", entry.onlinePlayersCount(), 100);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public enum ProfileOption{
|
||||
PRIORITY,
|
||||
CONDITION,
|
||||
MOTD,
|
||||
FAVICON,
|
||||
PLAYERCOUNT_HIDEPLAYERS,
|
||||
PLAYERCOUNT_HIDEPLAYERSHOVER,
|
||||
PLAYERCOUNT_HOVER,
|
||||
PLAYERCOUNT_TEXT,
|
||||
PLAYERCOUNT_EXTRAPLAYERS_ENABLED,
|
||||
PLAYERCOUNT_EXTRAPLAYERS_AMOUNT,
|
||||
PLAYERCOUNT_MAXPLAYERS_ENABLED,
|
||||
PLAYERCOUNT_MAXPLAYERS_AMOUNT,
|
||||
PLAYERCOUNT_ONLINEPLAYERS_ENABLED,
|
||||
PLAYERCOUNT_ONLINEPLAYERS_AMOUNT;
|
||||
|
||||
public static List<String> list(){
|
||||
return Arrays.stream(values())
|
||||
.map(ProfileOption::toString)
|
||||
.toList();
|
||||
}
|
||||
|
||||
public static ProfileOption fromString(String input){
|
||||
for(ProfileOption option : values()){
|
||||
if(option.toString().equalsIgnoreCase(input))
|
||||
return option;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return this.name().toLowerCase(Locale.ROOT).replace("_", ".");
|
||||
}
|
||||
|
||||
public String toDialogKey(){
|
||||
return this.name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package ch.andre601.advancedserverlist.core.commands;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
|
||||
import org.incendo.cloud.annotations.Command;
|
||||
import org.incendo.cloud.annotations.CommandDescription;
|
||||
import org.incendo.cloud.annotations.Permission;
|
||||
|
||||
|
||||
@Command("advancedserverlist|asl")
|
||||
@CommandDescription("Main Command of the plugin")
|
||||
@Permission("advancedserverlist.admin")
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public record CmdReload(AdvancedServerList<?> core){
|
||||
|
||||
public static CmdReload create(AdvancedServerList<?> core){
|
||||
return new CmdReload(core);
|
||||
}
|
||||
|
||||
@Command("reload")
|
||||
@CommandDescription("Reloads the plugin's main config and its profiles.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.reload"})
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public void handle(CmdSender sender){
|
||||
sender.sendPrefixedMsg("Reloading plugin...");
|
||||
|
||||
if(core().fileHandler().reloadConfig()){
|
||||
sender.sendPrefixedMsg("<green>Reloaded </green>config.yml<green>!");
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Encountered an error while reloading the </red>config.yml<red>.");
|
||||
sender.sendErrorMsg("<red>See the server console for details.");
|
||||
}
|
||||
|
||||
if(core().fileHandler().reloadProfiles()){
|
||||
sender.sendPrefixedMsg("<green>(Re)loaded <white>%d</white> Profile(s)!", core().fileHandler().profiles().size());
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Encountered errors while (re)loading profiles.");
|
||||
sender.sendErrorMsg("<red>See the server console for details.");
|
||||
}
|
||||
|
||||
sender.sendPrefixedMsg("<green>Reload complete!");
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
* Copyright (c) 2022-2025 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
|
||||
@@ -20,121 +20,329 @@
|
||||
* 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.commands;
|
||||
|
||||
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.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.PluginCommand;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
|
||||
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
import org.incendo.cloud.key.CloudKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
public class CommandHandler{
|
||||
|
||||
private final List<PluginCommand> subCommands = new ArrayList<>();
|
||||
public static final CloudKey<CommandType.Type> COMMAND_TYPE = CloudKey.of("command_type", CommandType.Type.class);
|
||||
public static final CloudKey<String> OPTION = CloudKey.of("profile_option", String.class);
|
||||
|
||||
public CommandHandler(AdvancedServerList<?> core){
|
||||
subCommands.add(new Help());
|
||||
subCommands.add(new Reload(core));
|
||||
subCommands.add(new ClearCache(core));
|
||||
}
|
||||
|
||||
public void handle(CmdSender sender, String[] args){
|
||||
if(args.length == 0){
|
||||
sender.sendErrorMsg("<red>Too few arguments! Use <grey>/asl help</grey> for a list of commands.");
|
||||
public static void handleSet(AdvancedServerList<?> core, CommandContext<CmdSender> context){
|
||||
CmdSender sender = context.sender();
|
||||
String profile = context.get("profile");
|
||||
String value = getOptionValue(context.getOrDefault("value", null));
|
||||
String option = context.command().commandMeta().getOrDefault(OPTION, "");
|
||||
|
||||
ServerListProfile slp = profile(core, profile);
|
||||
if(slp == null){
|
||||
sender.sendErrorMsg("<red>No profile with name</red> %s <red>found!", profile);
|
||||
return;
|
||||
}
|
||||
|
||||
for(PluginCommand subCommand : subCommands){
|
||||
if(subCommand.getArgument().equalsIgnoreCase(args[0])){
|
||||
if(!sender.hasPermission(subCommand.getPermission())){
|
||||
sender.sendErrorMsg(
|
||||
"<red>You do not have the permission <grey>%s</grey> to execute this command.",
|
||||
subCommand.getPermission()
|
||||
);
|
||||
return;
|
||||
int priority = slp.priority();
|
||||
String condition = slp.condition();
|
||||
ProfileEntry entry = slp.defaultProfile();
|
||||
ProfileEntry.Builder builder = slp.defaultProfile().builder();
|
||||
|
||||
List<String> oldValues = new ArrayList<>();
|
||||
List<String> newValues = new ArrayList<>();
|
||||
|
||||
switch(CmdProfiles.ProfileOption.fromString(option)){
|
||||
case PRIORITY -> {
|
||||
oldValues.add(String.valueOf(slp.priority()));
|
||||
|
||||
if(value == null || value.isEmpty()){
|
||||
priority = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
subCommand.handle(sender);
|
||||
try{
|
||||
priority = Integer.parseInt(value);
|
||||
|
||||
newValues.add(String.valueOf(priority));
|
||||
}catch(NumberFormatException ex){
|
||||
sender.sendErrorMsg("<red>Invalid value provided. Expected number but got</red> %s", value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
case CONDITION -> {
|
||||
oldValues.add(slp.condition());
|
||||
|
||||
if(value == null || value.isEmpty()){
|
||||
condition = "";
|
||||
break;
|
||||
}
|
||||
|
||||
condition = value;
|
||||
newValues.add(value);
|
||||
}
|
||||
case MOTD -> {
|
||||
oldValues.addAll(entry.motd());
|
||||
|
||||
if(value == null || value.isEmpty()){
|
||||
builder.motd(Collections.emptyList());
|
||||
break;
|
||||
}
|
||||
|
||||
String[] lines = value.split("\\\\n|<newline>|<br>");
|
||||
if(lines.length == 0){
|
||||
builder.motd(Collections.emptyList());
|
||||
break;
|
||||
}
|
||||
|
||||
if(lines.length > 1){
|
||||
if(lines.length > 2)
|
||||
sender.sendPrefixedMsg("<gold>Found %d Lines for MOTD. Only using first 2 lines...", lines.length);
|
||||
|
||||
builder.motd(Arrays.asList(lines));
|
||||
}else{
|
||||
builder.motd(Collections.singletonList(lines[0]));
|
||||
}
|
||||
|
||||
newValues.addAll(Arrays.asList(lines));
|
||||
}
|
||||
case FAVICON -> {
|
||||
oldValues.add(entry.favicon());
|
||||
|
||||
if(value == null || value.isEmpty()){
|
||||
builder.favicon("");
|
||||
break;
|
||||
}
|
||||
|
||||
builder.favicon(value);
|
||||
|
||||
newValues.add(value);
|
||||
}
|
||||
case PLAYERCOUNT_HIDEPLAYERS -> {
|
||||
if(!entry.hidePlayersEnabled().isNotSet())
|
||||
oldValues.add(String.valueOf(entry.hidePlayersEnabled().getOrDefault(false)));
|
||||
|
||||
if(value == null || value.isEmpty()){
|
||||
builder.hidePlayersEnabled(NullBool.NOT_SET);
|
||||
break;
|
||||
}
|
||||
|
||||
NullBool bool = NullBool.resolve(Boolean.parseBoolean(value));
|
||||
builder.hidePlayersEnabled(bool);
|
||||
|
||||
newValues.add(String.valueOf(bool.getOrDefault(false)));
|
||||
}
|
||||
case PLAYERCOUNT_HIDEPLAYERSHOVER -> {
|
||||
if(!entry.hidePlayersHoverEnabled().isNotSet())
|
||||
oldValues.add(String.valueOf(entry.hidePlayersHoverEnabled().getOrDefault(false)));
|
||||
|
||||
if(value == null || value.isEmpty()){
|
||||
builder.hidePlayersHoverEnabled(NullBool.NOT_SET);
|
||||
break;
|
||||
}
|
||||
|
||||
NullBool bool = NullBool.resolve(Boolean.parseBoolean(value));
|
||||
builder.hidePlayersHoverEnabled(bool);
|
||||
|
||||
newValues.add(String.valueOf(bool.getOrDefault(false)));
|
||||
}
|
||||
case PLAYERCOUNT_HOVER -> {
|
||||
oldValues.addAll(entry.players());
|
||||
|
||||
if(value == null || value.isEmpty()){
|
||||
builder.players(Collections.emptyList());
|
||||
break;
|
||||
}
|
||||
|
||||
String[] lines = value.split("\\\\n|<newline>");
|
||||
if(lines.length == 0){
|
||||
builder.players(Collections.emptyList());
|
||||
break;
|
||||
}
|
||||
|
||||
builder.players(Arrays.asList(lines));
|
||||
|
||||
newValues.addAll(Arrays.asList(lines));
|
||||
}
|
||||
case PLAYERCOUNT_TEXT -> {
|
||||
if(!entry.playerCountText().isEmpty())
|
||||
oldValues.add(entry.playerCountText());
|
||||
|
||||
if(value == null || value.isEmpty()){
|
||||
builder.playerCountText("");
|
||||
break;
|
||||
}
|
||||
|
||||
builder.playerCountText(value);
|
||||
|
||||
newValues.add(value);
|
||||
}
|
||||
case PLAYERCOUNT_EXTRAPLAYERS_ENABLED -> {
|
||||
if(!entry.extraPlayersEnabled().isNotSet())
|
||||
oldValues.add(String.valueOf(entry.extraPlayersEnabled().getOrDefault(false)));
|
||||
|
||||
if(value == null || value.isEmpty()){
|
||||
builder.extraPlayersEnabled(NullBool.NOT_SET);
|
||||
break;
|
||||
}
|
||||
|
||||
NullBool bool = NullBool.resolve(Boolean.parseBoolean(value));
|
||||
builder.extraPlayersEnabled(bool);
|
||||
|
||||
newValues.add(String.valueOf(bool.getOrDefault(false)));
|
||||
}
|
||||
case PLAYERCOUNT_EXTRAPLAYERS_AMOUNT -> {
|
||||
if(!entry.extraPlayersCount().isEmpty())
|
||||
oldValues.add(entry.extraPlayersCount());
|
||||
|
||||
if(value == null || value.isEmpty()){
|
||||
builder.extraPlayersCount("");
|
||||
break;
|
||||
}
|
||||
|
||||
builder.extraPlayersCount(value);
|
||||
|
||||
newValues.add(value);
|
||||
}
|
||||
case PLAYERCOUNT_MAXPLAYERS_ENABLED -> {
|
||||
if(!entry.maxPlayersEnabled().isNotSet())
|
||||
oldValues.add(String.valueOf(entry.maxPlayersEnabled().getOrDefault(false)));
|
||||
|
||||
if(value == null || value.isEmpty()){
|
||||
builder.maxPlayersEnabled(NullBool.NOT_SET);
|
||||
break;
|
||||
}
|
||||
|
||||
NullBool bool = NullBool.resolve(Boolean.parseBoolean(value));
|
||||
builder.maxPlayersEnabled(bool);
|
||||
|
||||
newValues.add(String.valueOf(bool.getOrDefault(false)));
|
||||
}
|
||||
case PLAYERCOUNT_MAXPLAYERS_AMOUNT -> {
|
||||
if(!entry.maxPlayersCount().isEmpty())
|
||||
oldValues.add(entry.extraPlayersCount());
|
||||
|
||||
if(value == null || value.isEmpty()){
|
||||
builder.maxPlayersCount("");
|
||||
break;
|
||||
}
|
||||
|
||||
builder.maxPlayersCount(value);
|
||||
|
||||
newValues.add(value);
|
||||
}
|
||||
case PLAYERCOUNT_ONLINEPLAYERS_ENABLED -> {
|
||||
if(!entry.onlinePlayersEnabled().isNotSet())
|
||||
oldValues.add(String.valueOf(entry.onlinePlayersEnabled().getOrDefault(false)));
|
||||
|
||||
if(value == null || value.isEmpty()){
|
||||
builder.onlinePlayersEnabled(NullBool.NOT_SET);
|
||||
break;
|
||||
}
|
||||
|
||||
NullBool bool = NullBool.resolve(Boolean.parseBoolean(value));
|
||||
builder.onlinePlayersEnabled(bool);
|
||||
|
||||
newValues.add(String.valueOf(bool.getOrDefault(false)));
|
||||
}
|
||||
case PLAYERCOUNT_ONLINEPLAYERS_AMOUNT -> {
|
||||
if(!entry.onlinePlayersCount().isEmpty())
|
||||
oldValues.add(entry.onlinePlayersCount());
|
||||
|
||||
if(value == null || value.isEmpty()){
|
||||
builder.onlinePlayersCount("");
|
||||
break;
|
||||
}
|
||||
|
||||
builder.onlinePlayersCount(value);
|
||||
|
||||
newValues.add(value);
|
||||
}
|
||||
case null, default -> {
|
||||
sender.sendErrorMsg("<red>Unknown option </red>%s<red>!", option);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sender.sendErrorMsg("<red>Unknown subcommand <grey>%s</grey>. Use <grey>/asl help</grey> for a list of commands.", args[0]);
|
||||
}
|
||||
|
||||
private static class Help extends PluginCommand{
|
||||
|
||||
public Help(){
|
||||
super("help");
|
||||
}
|
||||
|
||||
@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 config.yml and profiles");
|
||||
sender.sendMsg();
|
||||
sender.sendMsg("<aqua>/asl <white>clearCache <grey>- Clears the Player and Favicon cache");
|
||||
}
|
||||
}
|
||||
|
||||
private static class Reload extends PluginCommand{
|
||||
|
||||
private final AdvancedServerList<?> core;
|
||||
|
||||
public Reload(AdvancedServerList<?> core){
|
||||
super("reload");
|
||||
|
||||
this.core = core;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(CmdSender sender){
|
||||
sender.sendPrefixedMsg("Reloading plugin...");
|
||||
|
||||
if(core.getFileHandler().reloadConfig()){
|
||||
sender.sendPrefixedMsg("<green>Reloaded <grey>config.yml</grey>!");
|
||||
if(core.fileHandler().saveProfile(profileName(profile), priority, condition, slp.profiles(), builder.build())){
|
||||
if(sender.isPlayer()){
|
||||
sender.sendPrefixedMsg(
|
||||
"<green>Successfully updated <white>[<grey><hover:show_text:\"%s\">%s</hover></grey>]</white>!",
|
||||
changeValueHover(oldValues, newValues),
|
||||
option
|
||||
);
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Error while reloading <grey>config.yml</grey>!");
|
||||
sender.sendPrefixedMsg("<green>Successfully updated <white>[<grey>%s</grey>]</white>:", option);
|
||||
|
||||
if(oldValues.isEmpty()){
|
||||
sender.sendMsg("<red>-</red> <white><i>Not set</i></white>");
|
||||
}else{
|
||||
oldValues.forEach(oldValue -> sender.sendMsg("<red>-</red> %s", oldValue));
|
||||
}
|
||||
|
||||
sender.sendMsg();
|
||||
|
||||
if(newValues.isEmpty()){
|
||||
sender.sendMsg("<green>+</green> <white><i>Reset to default</i></white>");
|
||||
}else{
|
||||
newValues.forEach(newValue -> sender.sendMsg("<green>+</green> %s", newValue));
|
||||
}
|
||||
|
||||
sender.sendMsg();
|
||||
}
|
||||
|
||||
if(core.getFileHandler().reloadProfiles()){
|
||||
sender.sendPrefixedMsg("<green>Loaded <grey>%d profile(s)</grey>!", core.getFileHandler().getProfiles().size());
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Error while loading profile(s)!");
|
||||
}
|
||||
sender.sendPrefixedMsg(
|
||||
"<green>Use <white><click:suggest_command:/asl reload>/asl reload</click></white> to apply changes."
|
||||
);
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Encountered an IOException while trying to save file</red> %s<red>.", profile);
|
||||
sender.sendErrorMsg("<red>Check console for details.");
|
||||
|
||||
sender.sendPrefixedMsg("<green>Reload complete!");
|
||||
core.plugin().pluginLogger().warn("Encountered IOException while saving file <white>%s.yml</white>", profile);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ClearCache extends PluginCommand{
|
||||
|
||||
private final AdvancedServerList<?> core;
|
||||
|
||||
public ClearCache(AdvancedServerList<?> core){
|
||||
super("clearCache");
|
||||
|
||||
this.core = core;
|
||||
}
|
||||
public static ServerListProfile profile(AdvancedServerList<?> core, String name){
|
||||
return core.fileHandler().profiles().stream()
|
||||
.filter(p -> p.file().equalsIgnoreCase(profileName(name)))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(CmdSender sender){
|
||||
sender.sendPrefixedMsg("Clearing caches...");
|
||||
public static String profileName(String name){
|
||||
if(name == null || name.isEmpty())
|
||||
return "";
|
||||
|
||||
String filename = name.toLowerCase(Locale.ROOT);
|
||||
|
||||
return filename.endsWith(".yml") ? filename : filename + ".yml";
|
||||
}
|
||||
|
||||
private static String changeValueHover(List<String> oldValues, List<String> newValues){
|
||||
return """
|
||||
<red>-</red> %s<reset>
|
||||
|
||||
core.clearFaviconCache();
|
||||
sender.sendPrefixedMsg("<green>Successfully cleared Favicon Cache!");
|
||||
|
||||
core.clearPlayerCache();
|
||||
sender.sendPrefixedMsg("<green>Successfully cleared Player Cache!");
|
||||
|
||||
sender.sendPrefixedMsg("<green>Cache clearing complete!");
|
||||
}
|
||||
<green>+</green> %s""".formatted(
|
||||
oldValues.isEmpty() ? "<i>Not set</i>" : String.join("<reset>\n<red>-</red> ", oldValues),
|
||||
newValues.isEmpty() ? "<i>Not set</i>" : String.join("<reset>\n<green>+</green> ", newValues)
|
||||
);
|
||||
}
|
||||
|
||||
private static String getOptionValue(Object obj){
|
||||
return switch(obj){
|
||||
case null -> "";
|
||||
case Boolean bool -> Boolean.toString(bool);
|
||||
case Integer integer -> Integer.toString(integer);
|
||||
case String str -> str;
|
||||
default -> obj.toString();
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ch.andre601.advancedserverlist.core.commands;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.incendo.cloud.execution.postprocessor.CommandPostprocessingContext;
|
||||
import org.incendo.cloud.execution.postprocessor.CommandPostprocessor;
|
||||
import org.incendo.cloud.services.type.ConsumerService;
|
||||
|
||||
public class CommandTypePostprocessor implements CommandPostprocessor<CmdSender>{
|
||||
@Override
|
||||
public void accept(@NonNull CommandPostprocessingContext<CmdSender> context){
|
||||
CmdSender sender = context.commandContext().sender();
|
||||
CommandType.Type type = context.commandContext().command().commandMeta().get(CommandHandler.COMMAND_TYPE);
|
||||
|
||||
if(type == CommandType.Type.ALL || (type == CommandType.Type.PLAYER_ONLY && sender.isPlayer()))
|
||||
return;
|
||||
|
||||
sender.sendErrorMsg("<red>This command can only be executed by players!");
|
||||
ConsumerService.interrupt();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2025 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.commands;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.NullBool;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class ProfileOptionsSender{
|
||||
|
||||
private final List<OptionStringBuilder> builders = new ArrayList<>();
|
||||
private final String file;
|
||||
|
||||
public ProfileOptionsSender(String file){
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public ProfileOptionsSender append(OptionStringBuilder builder){
|
||||
this.builders.add(builder.file(file));
|
||||
return this;
|
||||
}
|
||||
|
||||
public void send(CmdSender sender){
|
||||
sender.sendMsg();
|
||||
sender.sendPrefixedMsg("Profile Info [<white>%s</white>]", file);
|
||||
for(OptionStringBuilder builder : builders){
|
||||
sender.sendMsg(builder.build());
|
||||
}
|
||||
sender.sendMsg();
|
||||
sender.sendMsg("[<grey>Hover for info, click for command</grey>]");
|
||||
sender.sendMsg();
|
||||
}
|
||||
|
||||
public static class OptionStringBuilder{
|
||||
|
||||
public static OptionStringBuilder of(String displayName, String hover){
|
||||
return new OptionStringBuilder(displayName)
|
||||
.hover(hover == null || hover.isEmpty() ? "<i>Not set</i>" : hover)
|
||||
.color(hover == null || hover.isEmpty() ? ColorName.RED : ColorName.GREEN);
|
||||
}
|
||||
|
||||
public static OptionStringBuilder of(String displayName, List<String> hover){
|
||||
return new OptionStringBuilder(displayName)
|
||||
.hover(hover == null || hover.isEmpty() ? "<i>Not set</i>" : String.join("\n<reset>", hover))
|
||||
.color(hover == null || hover.isEmpty() ? ColorName.RED : ColorName.GREEN);
|
||||
}
|
||||
|
||||
public static OptionStringBuilder of(String displayName, NullBool hover){
|
||||
return new OptionStringBuilder(displayName)
|
||||
.hover(hover == null || !hover.getOrDefault(false) ? "<red>Disabled</red>" : "<green>Enabled</green>")
|
||||
.color(hover == null || !hover.getOrDefault(false) ? ColorName.RED : ColorName.GREEN);
|
||||
}
|
||||
|
||||
public static OptionStringBuilder of(String displayName, int hover){
|
||||
return new OptionStringBuilder(displayName)
|
||||
.hover(String.valueOf(hover));
|
||||
}
|
||||
|
||||
public static OptionStringBuilder of(String displayName){
|
||||
return new OptionStringBuilder(displayName).hover(null);
|
||||
}
|
||||
|
||||
private final String displayName;
|
||||
private String file = null;
|
||||
private String prefix = "├─";
|
||||
private ColorName color = ColorName.AQUA;
|
||||
private String hover = "<i>Not set</i>";
|
||||
private CmdProfiles.ProfileOption option = null;
|
||||
|
||||
private OptionStringBuilder(String displayName){
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public OptionStringBuilder file(String file){
|
||||
this.file = file;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OptionStringBuilder prefix(String prefix){
|
||||
this.prefix = prefix;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OptionStringBuilder color(ColorName color){
|
||||
this.color = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OptionStringBuilder hover(String hover){
|
||||
this.hover = hover;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OptionStringBuilder option(CmdProfiles.ProfileOption option){
|
||||
this.option = option;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String build(){
|
||||
StringBuilder builder = new StringBuilder("<grey>")
|
||||
.append(prefix)
|
||||
.append(" <white>[")
|
||||
.append('<')
|
||||
.append(color)
|
||||
.append('>');
|
||||
|
||||
if(hover != null && !hover.isEmpty()){
|
||||
builder.append("<hover:show_text:\"")
|
||||
.append(hover)
|
||||
.append("\">");
|
||||
}
|
||||
|
||||
if(option != null && file != null && !file.isEmpty()){
|
||||
builder.append("<click:suggest_command:/asl profiles set ")
|
||||
.append(file)
|
||||
.append(" ")
|
||||
.append(option)
|
||||
.append(" >");
|
||||
}
|
||||
|
||||
builder.append(displayName);
|
||||
|
||||
if(option != null && file != null && !file.isEmpty())
|
||||
builder.append("</click>");
|
||||
|
||||
if(hover != null && !hover.isEmpty())
|
||||
builder.append("</hover>");
|
||||
|
||||
builder.append("</")
|
||||
.append(color)
|
||||
.append(">]</white></grey>");
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public enum ColorName{
|
||||
RED,
|
||||
GREEN,
|
||||
AQUA;
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return this.name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
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;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@NullMarked
|
||||
public class MaintenancePlaceholder extends PlaceholderProvider{
|
||||
|
||||
private final AdvancedServerList<?> plugin;
|
||||
|
||||
public MaintenancePlaceholder(AdvancedServerList<?> plugin){
|
||||
super("maintenance");
|
||||
|
||||
this.plugin = plugin;
|
||||
}
|
||||
@Override
|
||||
public @Nullable String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
|
||||
if(!placeholder.equals("maintenanceEnabled") || !plugin.plugin().isPluginEnabled("Maintenance"))
|
||||
return null;
|
||||
|
||||
return String.valueOf(MaintenanceUtil.get().isMaintenanceEnabled());
|
||||
}
|
||||
}
|
||||
@@ -30,12 +30,21 @@ import eu.kennytv.maintenance.api.MaintenanceProvider;
|
||||
|
||||
public class MaintenanceUtil{
|
||||
|
||||
private static MaintenanceUtil instance;
|
||||
|
||||
private final Maintenance api;
|
||||
|
||||
public MaintenanceUtil(){
|
||||
private MaintenanceUtil(){
|
||||
this.api = MaintenanceProvider.get();
|
||||
}
|
||||
|
||||
public static MaintenanceUtil get(){
|
||||
if(instance != null)
|
||||
return instance;
|
||||
|
||||
return (instance = new MaintenanceUtil());
|
||||
}
|
||||
|
||||
public boolean isMaintenanceEnabled(){
|
||||
return api.isMaintenance();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package ch.andre601.advancedserverlist.core.compat.miniplaceholders;
|
||||
|
||||
import io.github.miniplaceholders.api.MiniPlaceholders;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
|
||||
public class MiniPlaceholdersUtil{
|
||||
|
||||
public static TagResolver tagResolvers(){
|
||||
return MiniPlaceholders.globalPlaceholders();
|
||||
}
|
||||
}
|
||||
@@ -25,31 +25,42 @@
|
||||
|
||||
package ch.andre601.advancedserverlist.core.compat.papi;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.objects.ValueCache;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import net.william278.papiproxybridge.api.PlaceholderAPI;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class PAPIUtil{
|
||||
|
||||
private static PAPIUtil instance;
|
||||
|
||||
private final ValueCache<CompletableFuture<Set<String>>> serversCache = new ValueCache<>(Duration.ofSeconds(10));
|
||||
private final Cache<CacheKey, CompletableFuture<String>> textCache = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(Duration.ofSeconds(10))
|
||||
.build();
|
||||
private final PlaceholderAPI papi;
|
||||
|
||||
private final PAPICache cache = new PAPICache();
|
||||
|
||||
public PAPIUtil(){
|
||||
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("findServers");
|
||||
papi.getClass().getMethod("getServers");
|
||||
return true;
|
||||
}catch(NoSuchMethodException ex){
|
||||
return false;
|
||||
@@ -57,34 +68,27 @@ public class PAPIUtil{
|
||||
}
|
||||
|
||||
public String getServer(){
|
||||
return cache.get(() -> {
|
||||
List<String> servers;
|
||||
try{
|
||||
servers = papi.findServers().getNow(Collections.emptyList());
|
||||
}catch(CancellationException | CompletionException ex){
|
||||
return null;
|
||||
}
|
||||
|
||||
if(servers == null || servers.isEmpty())
|
||||
return null;
|
||||
|
||||
return servers.get(0);
|
||||
});
|
||||
Set<String> values = serversCache.get(papi::getServers).getNow(Collections.emptySet());
|
||||
if(values.isEmpty())
|
||||
return null;
|
||||
|
||||
return List.copyOf(values).getFirst();
|
||||
}
|
||||
|
||||
public <P> P getPlayer(Collection<P> players){
|
||||
if(players.isEmpty())
|
||||
return null;
|
||||
|
||||
return List.copyOf(players).get(0);
|
||||
return List.copyOf(players).getFirst();
|
||||
}
|
||||
|
||||
public String parse(String text, UUID carrier, UUID player){
|
||||
try{
|
||||
CompletableFuture<String> future = papi.formatPlaceholders(text, carrier, player);
|
||||
return future.getNow(text);
|
||||
}catch(IllegalArgumentException | CancellationException | CompletionException ex){
|
||||
return textCache.get(new CacheKey(player, text), () -> papi.formatPlaceholders(text, carrier, player)).getNow(text);
|
||||
}catch(ExecutionException ex){
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
private record CacheKey(UUID uuid, String text){}
|
||||
}
|
||||
@@ -29,8 +29,6 @@ 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.compat.maintenance.MaintenanceUtil;
|
||||
import ch.andre601.advancedserverlist.core.compat.papi.PAPIUtil;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.events.GenericEventWrapper;
|
||||
@@ -38,146 +36,193 @@ import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
|
||||
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
|
||||
import ch.andre601.advancedserverlist.core.profiles.profile.ProfileManager;
|
||||
import ch.andre601.advancedserverlist.core.profiles.replacer.StringReplacer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.JoinConfiguration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PingEventHandler{
|
||||
|
||||
private static PAPIUtil papiUtil = null;
|
||||
private static MaintenanceUtil maintenanceUtil = null;
|
||||
|
||||
public static <F, P extends GenericPlayer> void handleEvent(GenericEventWrapper<F, P> event){
|
||||
event.getPlugin().getPluginLogger().debug(PingEventHandler.class, "Received ping event. Handling it...");
|
||||
if(event.isInvalidProtocol() || event.isMaintenanceModeActive()){
|
||||
event.getPlugin().getPluginLogger().debug(PingEventHandler.class, "Not handling event. Either protocol was invalid of Maintenance is enabled.");
|
||||
return;
|
||||
public static <F, P extends GenericPlayer> ProfileEntry handleEvent(GenericEventWrapper<F, P> event){
|
||||
event.plugin().pluginLogger().debug(PingEventHandler.class, "Received ping event. Handling it...");
|
||||
if(event.isInvalidProtocol()){
|
||||
event.plugin().pluginLogger().debug(PingEventHandler.class, "Not handling event. Protocol was invalid.");
|
||||
return null;
|
||||
}
|
||||
|
||||
PluginCore<F> plugin = event.getPlugin();
|
||||
String host = event.getVirtualHost();
|
||||
PluginLogger logger = plugin.getPluginLogger();
|
||||
PluginCore<F> plugin = event.plugin();
|
||||
String host = event.virtualHost();
|
||||
PluginLogger logger = plugin.pluginLogger();
|
||||
|
||||
logger.debug(PingEventHandler.class, "Protocol valid. Continue handling...");
|
||||
|
||||
int online = event.getOnlinePlayers();
|
||||
int max = event.getMaxPlayers();
|
||||
int online = event.onlinePlayers();
|
||||
int max = event.maxPlayers();
|
||||
|
||||
P player = event.createPlayer(
|
||||
plugin.getCore().getPlayerHandler().getCachedPlayer(event.getPlayerIP()),
|
||||
event.getProtocolVersion()
|
||||
plugin.core().playerHandler().getCachedPlayer(event.playerIP()),
|
||||
event.protocolVersion()
|
||||
);
|
||||
GenericServer server = event.createGenericServer(online, max, host);
|
||||
GenericServer server = event.createServer(online, max, host);
|
||||
|
||||
ServerListProfile profile = ProfileManager.resolveProfile(plugin.getCore(), player, server);
|
||||
ServerListProfile profile = ProfileManager.resolveProfile(plugin.core(), player, server);
|
||||
|
||||
if(profile == null){
|
||||
logger.debugWarn(PingEventHandler.class, "Server List Profile couldn't be resolved properly. Cancelling event handling...");
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.debug(PingEventHandler.class, "Received valid Server List Profile. Calling PreServerListSetEvent...");
|
||||
|
||||
GenericServerListEvent e = event.callEvent(ProfileManager.merge(profile));
|
||||
if(e == null || e.isCancelled()){
|
||||
GenericServerListEvent slEvent = event.callEvent(ProfileManager.merge(profile));
|
||||
if(slEvent == null || slEvent.isCancelled()){
|
||||
logger.debug(PingEventHandler.class, "PreServerListSetEvent was cancelled. Stopping ping handling...");
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.debug(PingEventHandler.class, "PreServerListSetEvent completed. Proceeding with ping handling...");
|
||||
|
||||
ProfileEntry entry = e.getEntry();
|
||||
ProfileEntry entry = slEvent.getEntry();
|
||||
if(entry.isInvalid()){
|
||||
logger.debugWarn(PingEventHandler.class, "No valid ProfileEntry retrieved. Cancelling ping handling...");
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean extraPlayers = ProfileManager.checkOption(entry.extraPlayersEnabled());
|
||||
|
||||
if(extraPlayers){
|
||||
max = online + (entry.extraPlayersCount() == null ? 0 : entry.extraPlayersCount());
|
||||
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))
|
||||
.applyMMTags(event.plugin())
|
||||
.toString();
|
||||
|
||||
logger.debug(PingEventHandler.class, "Extra Players enabled. Applying '%d' as max player count...", max);
|
||||
|
||||
event.setMaxPlayers(max);
|
||||
try{
|
||||
online = onlinePlayers == null ? online : Integer.parseInt(onlinePlayers);
|
||||
event.onlinePlayers(online);
|
||||
}catch(NumberFormatException ex){
|
||||
logger.warn("Option 'playerCount -> onlinePlayers -> amount' is not a valid Number!");
|
||||
}
|
||||
}
|
||||
|
||||
if(ProfileManager.checkOption(entry.maxPlayersEnabled()) && !extraPlayers){
|
||||
max = (entry.maxPlayersCount() == null) ? 0 : entry.maxPlayersCount();
|
||||
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))
|
||||
.applyMMTags(event.plugin())
|
||||
.toString();
|
||||
|
||||
logger.debug(PingEventHandler.class, "Max Players enabled. Applying '%d' as max player count...", max);
|
||||
|
||||
event.setMaxPlayers(max);
|
||||
try{
|
||||
max = online + (extraPlayers == null ? 0 : Integer.parseInt(extraPlayers));
|
||||
|
||||
logger.debug(PingEventHandler.class, "'playerCount -> extraPlayers -> amount' is a valid number. Applying it...");
|
||||
event.maxPlayers(max);
|
||||
}catch(NumberFormatException ex){
|
||||
logger.warn("Option 'playerCount -> extraPlayers -> amount' is not a valid Number!");
|
||||
}
|
||||
}
|
||||
|
||||
GenericServer finalServer = event.createGenericServer(online, max, host);
|
||||
|
||||
if(ProfileManager.checkOption(entry.motd())){
|
||||
logger.debug(PingEventHandler.class, "MOTD set. Applying '%s'...", String.join("\\n", entry.motd()));
|
||||
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))
|
||||
.applyMMTags(event.plugin())
|
||||
.toString();
|
||||
|
||||
event.setMotd(
|
||||
ComponentParser.list(entry.motd())
|
||||
try{
|
||||
max = maxPlayers == null ? 0 : Integer.parseInt(maxPlayers);
|
||||
|
||||
logger.debug(PingEventHandler.class, "'playerCount -> maxPlayers -> amount' is a valid number. Applying it...");
|
||||
event.maxPlayers(max);
|
||||
}catch(NumberFormatException ex){
|
||||
logger.warn("Option 'playerCount -> maxPlayers -> amount' is not a valid Number!");
|
||||
}
|
||||
}
|
||||
|
||||
GenericServer finalServer = event.createServer(online, max, host);
|
||||
|
||||
if(ProfileManager.checkOption(entry.motd()) && ignoreMaintenance(event, "motd")){
|
||||
logger.debug(PingEventHandler.class, "'motd' option present. Applying ['%s']...", String.join("', '", entry.motd()));
|
||||
|
||||
List<Component> motd = entry.motd().stream()
|
||||
.map(line -> ComponentParser.text(line)
|
||||
.modifyText(text -> StringReplacer.replace(text, player, finalServer))
|
||||
.modifyText(text -> event.parsePAPIPlaceholders(text, player))
|
||||
.modifyText(text -> plugin.core().textCenterUtil().getCenteredText(text))
|
||||
.applyMMTags(event.plugin())
|
||||
.toComponent()
|
||||
);
|
||||
)
|
||||
.toList();
|
||||
|
||||
event.motd(Component.join(JoinConfiguration.separator(Component.newline()), motd));
|
||||
}
|
||||
|
||||
boolean hidePlayers = ProfileManager.checkOption(entry.hidePlayersEnabled());
|
||||
|
||||
if(hidePlayers){
|
||||
logger.debug(PingEventHandler.class, "Hide Players enabled. Hiding player count...");
|
||||
if(hidePlayers && ignoreMaintenance(event, "hidePlayers") && ignoreMaintenance(event, "hidePlayersHover")){
|
||||
logger.debug(PingEventHandler.class, "'playerCount -> hidePlayers' enabled. Hiding player count...");
|
||||
|
||||
event.hidePlayers();
|
||||
}
|
||||
|
||||
if(ProfileManager.checkOption(entry.playerCountText()) && !hidePlayers){
|
||||
logger.debug(PingEventHandler.class, "Player Count Text set. Applying '%s'...", entry.playerCountText());
|
||||
if(ProfileManager.checkOption(entry.playerCountText()) && !hidePlayers && ignoreMaintenance(event, "playerCountText")){
|
||||
logger.debug(PingEventHandler.class, "'playerCount -> text' option set. Applying '%s'...", entry.playerCountText());
|
||||
|
||||
event.setPlayerCount(
|
||||
event.playerCount(
|
||||
ComponentParser.text(entry.playerCountText())
|
||||
.modifyText(text -> StringReplacer.replace(text, player, finalServer))
|
||||
.modifyText(text -> event.parsePAPIPlaceholders(text, player))
|
||||
.applyMMTags(event.plugin())
|
||||
.toString()
|
||||
);
|
||||
}
|
||||
|
||||
if(ProfileManager.checkOption(entry.players()) && !hidePlayers){
|
||||
logger.debug(PingEventHandler.class, "Player Count Hover set. Applying '%s'...", String.join("\\n", entry.players()));
|
||||
|
||||
event.setPlayers(entry.players(), player, server);
|
||||
boolean hidePlayersHover = ProfileManager.checkOption(entry.hidePlayersHoverEnabled());
|
||||
if(hidePlayersHover){
|
||||
logger.debug(PingEventHandler.class, "'playerCount -> hidePlayersHover' option set. Hiding Player Hover...");
|
||||
event.playersHidden();
|
||||
}
|
||||
|
||||
if(ProfileManager.checkOption(entry.favicon())){
|
||||
logger.debug(PingEventHandler.class, "Favicon set. Resolving '%s'...", entry.favicon());
|
||||
if(ProfileManager.checkOption(entry.players()) && !hidePlayers && !hidePlayersHover && ignoreMaintenance(event, "playerCountHover")){
|
||||
logger.debug(PingEventHandler.class, "'playerCount -> hover' option set. Applying ['%s']...", String.join("', '", entry.players()));
|
||||
|
||||
String faviconString = StringReplacer.replace(entry.favicon(), player, server);
|
||||
event.players(entry.players(), player, server);
|
||||
}
|
||||
|
||||
if(ProfileManager.checkOption(entry.favicon()) && ignoreMaintenance(event, "favicon")){
|
||||
logger.debug(PingEventHandler.class, "'favicon' option set. Resolving '%s'...", entry.favicon());
|
||||
|
||||
F favicon = plugin.getFaviconHandler().getFavicon(faviconString);
|
||||
String faviconString = StringReplacer.modifier(entry.favicon())
|
||||
.modify(text -> StringReplacer.replace(text, player, server))
|
||||
.modify(text -> event.parsePAPIPlaceholders(text, player))
|
||||
.asString();
|
||||
|
||||
F favicon = plugin.faviconHandler().favicon(faviconString);
|
||||
|
||||
if(favicon == null){
|
||||
logger.debugWarn(PingEventHandler.class, "Favicon was invalid or not yet resolved! Using default favicon of Server/Proxy...");
|
||||
|
||||
event.setDefaultFavicon();
|
||||
event.defaultFavicon();
|
||||
}else{
|
||||
logger.debug(PingEventHandler.class, "Applying favicon...");
|
||||
|
||||
event.setFavicon(favicon);
|
||||
event.favicon(favicon);
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug(PingEventHandler.class, "Event handling completed. Updating Ping data...");
|
||||
event.updateEvent();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
public static PAPIUtil getPAPIUtil(){
|
||||
if(papiUtil != null)
|
||||
return papiUtil;
|
||||
private static <F, P extends GenericPlayer> boolean ignoreMaintenance(GenericEventWrapper<F, P> event, String option){
|
||||
if(!event.isMaintenanceModeActive())
|
||||
return true;
|
||||
|
||||
return (papiUtil = new PAPIUtil());
|
||||
}
|
||||
|
||||
public static MaintenanceUtil getMaintenanceUtil(){
|
||||
if(maintenanceUtil != null)
|
||||
return maintenanceUtil;
|
||||
|
||||
return (maintenanceUtil = new MaintenanceUtil());
|
||||
return !event.plugin().core().fileHandler().getBool(true, "disableDuringMaintenance", option);
|
||||
}
|
||||
}
|
||||
@@ -46,12 +46,80 @@ public class ConfigMigrator{
|
||||
public static ConfigurationTransformation.Versioned create(){
|
||||
return ConfigurationTransformation.versionedBuilder()
|
||||
.versionKey("configVersion")
|
||||
.addVersion(LATEST, threeToFour())
|
||||
.addVersion(LATEST, nineToTen())
|
||||
.addVersion(9, eightToNine())
|
||||
.addVersion(8, sevenToEight())
|
||||
.addVersion(7, sixToSeven())
|
||||
.addVersion(6, fiveToSix())
|
||||
.addVersion(5, fourToFive())
|
||||
.addVersion(4, threeToFour())
|
||||
.addVersion(3, twoToThree())
|
||||
.addVersion(2, oneToTwo())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static ConfigurationTransformation nineToTen(){
|
||||
return ConfigurationTransformation.builder()
|
||||
.addAction(NodePath.path("faviconStrategy"), TransformAction.rename("faviconResizeStrategy"))
|
||||
.build();
|
||||
}
|
||||
|
||||
public static ConfigurationTransformation eightToNine(){
|
||||
return ConfigurationTransformation.builder()
|
||||
.addAction(NodePath.path(), (path, value) -> {
|
||||
value.node("faviconStrategy").set("resize");
|
||||
|
||||
return null;
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
public static ConfigurationTransformation sevenToEight(){
|
||||
return ConfigurationTransformation.builder()
|
||||
.addAction(NodePath.path("disableDuringMaintenance"), (path, value) -> {
|
||||
value.node("hidePlayersHover").set(true);
|
||||
|
||||
return null;
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
public static ConfigurationTransformation sixToSeven(){
|
||||
return ConfigurationTransformation.builder()
|
||||
.addAction(NodePath.path(), (path, value) -> {
|
||||
value.node("printBanner").set(true);
|
||||
|
||||
return null;
|
||||
})
|
||||
.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) -> {
|
||||
@@ -94,7 +162,7 @@ public class ConfigMigrator{
|
||||
versioned.apply(node);
|
||||
final int endVersion = versioned.version(node);
|
||||
if(startVersion < endVersion)
|
||||
logger.info("Migrated config.yml from v" + startVersion + " to v" + endVersion);
|
||||
logger.success("Successfully migrated config from <white>v%d</white> to <white>v%d</white>", startVersion, endVersion);
|
||||
}
|
||||
|
||||
return node;
|
||||
|
||||
@@ -32,6 +32,7 @@ import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
|
||||
import ch.andre601.advancedserverlist.core.profiles.profile.ProfileSerializer;
|
||||
import org.spongepowered.configurate.ConfigurateException;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
import org.spongepowered.configurate.yaml.NodeStyle;
|
||||
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
|
||||
|
||||
@@ -46,6 +47,7 @@ import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class FileHandler{
|
||||
|
||||
@@ -61,19 +63,27 @@ public class FileHandler{
|
||||
|
||||
public FileHandler(AdvancedServerList<?> core){
|
||||
this.plugin = core;
|
||||
this.logger = core.getPlugin().getPluginLogger();
|
||||
this.logger = core.plugin().pluginLogger();
|
||||
|
||||
this.config = core.getPlugin().getFolderPath().resolve("config.yml");
|
||||
this.profilesFolder = core.getPlugin().getFolderPath().resolve("profiles");
|
||||
this.config = core.plugin().folderPath().resolve("config.yml");
|
||||
this.profilesFolder = core.plugin().folderPath().resolve("profiles");
|
||||
}
|
||||
|
||||
public List<ServerListProfile> getProfiles(){
|
||||
public List<ServerListProfile> profiles(){
|
||||
return profiles;
|
||||
}
|
||||
|
||||
public ServerListProfile profile(String file){
|
||||
for(ServerListProfile profile : profiles){
|
||||
if(profile.file().equalsIgnoreCase(file))
|
||||
return profile;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean loadConfig(){
|
||||
logger.info("Loading config.yml...");
|
||||
File folder = plugin.getPlugin().getFolderPath().toFile();
|
||||
File folder = plugin.plugin().folderPath().toFile();
|
||||
if(!folder.exists() && !folder.mkdirs()){
|
||||
logger.warn("Couldn't create folder for plugin. Is it missing Write permissions?");
|
||||
return false;
|
||||
@@ -87,7 +97,7 @@ public class FileHandler{
|
||||
}
|
||||
|
||||
Files.copy(stream, config);
|
||||
logger.info("Created new config.yml!");
|
||||
logger.success("Created new config.yml!");
|
||||
}catch(IOException ex){
|
||||
logger.warn("Cannot create config.yml for plugin.", ex);
|
||||
return false;
|
||||
@@ -98,24 +108,79 @@ public class FileHandler{
|
||||
}
|
||||
|
||||
public boolean loadProfiles(){
|
||||
logger.info("Loading profiles...");
|
||||
if(!profilesFolder.toFile().exists() && 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;
|
||||
}
|
||||
logger.info("[-] Loading profiles...");
|
||||
|
||||
if(createFile("default.yml")){
|
||||
return reloadProfiles();
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean saveProfile(String file, int priority, String condition, List<ProfileEntry> profiles, ProfileEntry defaultProfile){
|
||||
String filename = file.toLowerCase(Locale.ROOT);
|
||||
Path filePath = profilesFolder.resolve(filename.endsWith(".yml") ? filename : filename + ".yml");
|
||||
if(!Files.exists(filePath)){
|
||||
logger.warn("Cannot find profile with file name '<white>%s</white>'", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
return reloadProfiles();
|
||||
YamlConfigurationLoader loader = YamlConfigurationLoader.builder()
|
||||
.defaultOptions(opts -> opts.serializers(build -> build.register(ProfileEntry.class, ProfileSerializer.INSTANCE)))
|
||||
.path(filePath)
|
||||
.nodeStyle(NodeStyle.BLOCK)
|
||||
.build();
|
||||
|
||||
ConfigurationNode node;
|
||||
try{
|
||||
node = loader.load();
|
||||
}catch(IOException ex){
|
||||
logger.warn("Encountered an IOException while loading file '<white>%s</white>'", ex, filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(node == null)
|
||||
return false;
|
||||
|
||||
try{
|
||||
node.node("priority").set(priority);
|
||||
node.node("condition").set(condition);
|
||||
node.node("profiles").setList(ProfileEntry.class, profiles);
|
||||
node.set(ProfileEntry.class, defaultProfile);
|
||||
|
||||
loader.save(node);
|
||||
return true;
|
||||
}catch(SerializationException ex){
|
||||
logger.warn("Encountered a SerializationException while saving file '<white>%s</white>'", ex, filename);
|
||||
return false;
|
||||
}catch(IOException ex){
|
||||
logger.warn("Encountered an IOException while saving file '<white>%s</white>'", ex, filename);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean createFile(String name){
|
||||
if(Files.notExists(profilesFolder) && profilesFolder.toFile().mkdirs()){
|
||||
logger.debugSuccess(FileHandler.class, "Successfully created profiles folder.");
|
||||
}
|
||||
|
||||
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 <white>%s</white>", ex, name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean migrateConfig(){
|
||||
@@ -147,7 +212,7 @@ public class FileHandler{
|
||||
|
||||
File[] files = profilesFolder.toFile().listFiles(((dir, name) -> name.endsWith(".yml")));
|
||||
if(files == null || files.length == 0){
|
||||
logger.warn("Cannot load files from profiles folder! No valid YAML files present.");
|
||||
logger.failure("Cannot load files from profiles folder! No valid YAML files present.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -157,11 +222,11 @@ public class FileHandler{
|
||||
continue;
|
||||
|
||||
profiles.add(ServerListProfile.Builder.resolve(file.getName(), tmp, logger).build());
|
||||
logger.info("Loaded '%s'!", file.getName());
|
||||
logger.debugSuccess(FileHandler.class, "Loaded '<white>%s</white>'!", file.getName());
|
||||
}
|
||||
|
||||
if(profiles.isEmpty()){
|
||||
logger.warn("Couldn't load any profile from profiles folder!");
|
||||
logger.failure("Couldn't load any profile from profiles folder!");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -178,7 +243,7 @@ public class FileHandler{
|
||||
try{
|
||||
return loader.load();
|
||||
}catch(IOException ex){
|
||||
logger.warn("Cannot load '%s' due to an IOException!", ex, path.toFile().getName());
|
||||
logger.warn("Cannot load '<white>%s</white>' due to an IOException!", ex, path.toFile().getName());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -187,8 +252,12 @@ 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){
|
||||
@@ -201,11 +270,11 @@ public class FileHandler{
|
||||
}
|
||||
|
||||
private boolean makeBackup(){
|
||||
logger.info("Making backup of old config.yml...");
|
||||
logger.info("[-] Making backup of old config.yml...");
|
||||
|
||||
File backups = plugin.getPlugin().getFolderPath().resolve("backups").toFile();
|
||||
File backups = plugin.plugin().folderPath().resolve("backups").toFile();
|
||||
if(!backups.exists() && !backups.mkdirs()){
|
||||
logger.warn("Cannot create backups folder for migration!");
|
||||
logger.failure("Cannot create backups folder for migration!");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -214,16 +283,16 @@ public class FileHandler{
|
||||
File configBackup = new File(backups, "config_" + date.replace(":", "_") + ".yml");
|
||||
try{
|
||||
if(!configBackup.exists() && !configBackup.createNewFile()){
|
||||
logger.warn("Cannot create backup file for config.yml!");
|
||||
logger.failure("Cannot create backup file for config.yml!");
|
||||
return false;
|
||||
}
|
||||
|
||||
Files.copy(config, configBackup.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
logger.info("Saved backup as '%s'!", configBackup.getName());
|
||||
logger.success("Saved backup as '<white>%s</white>'!", configBackup.getName());
|
||||
|
||||
return true;
|
||||
}catch(IOException ex){
|
||||
logger.warn("Encountered IOException while trying to create a backup.", ex);
|
||||
logger.failure("Encountered IOException while trying to create a backup.", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,10 +27,18 @@ package ch.andre601.advancedserverlist.core.interfaces;
|
||||
|
||||
public interface PluginLogger{
|
||||
|
||||
default void debugSuccess(Class<?> clazz, String msg, Object... args){
|
||||
debug(clazz, "[<green>✓</green>] " + msg, args);
|
||||
}
|
||||
|
||||
void debug(Class<?> clazz, String msg, Object... args);
|
||||
|
||||
void debugWarn(Class<?> clazz, String msg, Object... args);
|
||||
|
||||
default void success(String msg, Object... args){
|
||||
info("[<green>✓</green>] " + msg, args);
|
||||
}
|
||||
|
||||
void info(String msg, Object... args);
|
||||
|
||||
void warn(String msg, Object... args);
|
||||
@@ -38,4 +46,8 @@ public interface PluginLogger{
|
||||
void warn(String msg, Throwable throwable);
|
||||
|
||||
void warn(String msg, Throwable throwable, Object... args);
|
||||
|
||||
default void failure(String msg, Object... args){
|
||||
warn("[<red>x</red>] " + msg, args);
|
||||
}
|
||||
}
|
||||
@@ -25,12 +25,12 @@
|
||||
|
||||
package ch.andre601.advancedserverlist.core.interfaces.commands;
|
||||
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
|
||||
public interface CmdSender{
|
||||
|
||||
String prefix = "<grey>[<gradient:aqua:white>AdvancedServerList</gradient>] ";
|
||||
String errorPrefix = "<grey>[<gradient:dark_red:red>AdvancedServerList</gradient>] ";
|
||||
|
||||
boolean hasPermission(String permission);
|
||||
String prefix = "<grey>[<aqua>ASL</aqua>] ";
|
||||
String errorPrefix = "<grey>[<red>ASL</red>] ";
|
||||
|
||||
void sendMsg(String msg, Object... args);
|
||||
|
||||
@@ -45,4 +45,8 @@ public interface CmdSender{
|
||||
default void sendErrorMsg(String msg, Object... args){
|
||||
sendMsg(errorPrefix + msg, args);
|
||||
}
|
||||
|
||||
Audience audience();
|
||||
|
||||
boolean isPlayer();
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
* Copyright (c) 2022-2025 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
|
||||
@@ -20,17 +20,15 @@
|
||||
* 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.bungeecord.listeners;
|
||||
package ch.andre601.advancedserverlist.core.interfaces.commands;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.bungeecord.events.PreServerListSetEvent;
|
||||
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.SenderMapper;
|
||||
|
||||
public class PreServerListSetEventImpl extends PreServerListSetEvent{
|
||||
public interface CommandManagerInterface<S>{
|
||||
CommandManager<CmdSender> commandHandler();
|
||||
|
||||
public PreServerListSetEventImpl(ProfileEntry entry){
|
||||
super(entry);
|
||||
}
|
||||
SenderMapper<S, CmdSender> senderMapper();
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
* Copyright (c) 2022-2025 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
|
||||
@@ -20,28 +20,22 @@
|
||||
* 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.interfaces.commands;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
public abstract class PluginCommand{
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
public @interface CommandType{
|
||||
Type value();
|
||||
|
||||
private final String argument;
|
||||
|
||||
public PluginCommand(String argument){
|
||||
this.argument = argument;
|
||||
enum Type{
|
||||
ALL,
|
||||
PLAYER_ONLY
|
||||
}
|
||||
|
||||
public String getArgument(){
|
||||
return argument;
|
||||
}
|
||||
|
||||
public String getPermission(){
|
||||
return "advancedserverlist.command." + getArgument().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
public abstract void handle(CmdSender sender);
|
||||
}
|
||||
@@ -26,16 +26,19 @@
|
||||
package ch.andre601.advancedserverlist.core.interfaces.core;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.dialogs.IDialog;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.dialogs.IDialogList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
|
||||
import ch.andre601.advancedserverlist.core.profiles.handlers.FaviconHandler;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public interface PluginCore<F>{
|
||||
|
||||
void loadCommands();
|
||||
|
||||
void loadEvents();
|
||||
|
||||
void loadMetrics();
|
||||
@@ -44,26 +47,38 @@ public interface PluginCore<F>{
|
||||
|
||||
void clearFaviconCache();
|
||||
|
||||
AdvancedServerList<F> getCore();
|
||||
void downloadLibrary(String groupId, String artifactId, String version);
|
||||
|
||||
Path getFolderPath();
|
||||
void startScheduler();
|
||||
|
||||
PluginLogger getPluginLogger();
|
||||
AdvancedServerList<F> core();
|
||||
|
||||
FaviconHandler<F> getFaviconHandler();
|
||||
Path folderPath();
|
||||
|
||||
String getPlatformName();
|
||||
PluginLogger pluginLogger();
|
||||
|
||||
String getPlatformVersion();
|
||||
FaviconHandler<F> faviconHandler();
|
||||
|
||||
String getLoader();
|
||||
CommandManager<CmdSender> commandManager();
|
||||
|
||||
String platformInfo();
|
||||
|
||||
String loader();
|
||||
|
||||
boolean isPluginEnabled(String plugin);
|
||||
|
||||
boolean isDialogsSupported();
|
||||
|
||||
F createFavicon(BufferedImage image) throws Exception;
|
||||
|
||||
IDialogList.IDialogListBuilder createDialogListBuilder(String title);
|
||||
|
||||
IDialog.IDialogBuilder createDialogBuilder(String title, ServerListProfile profile, int profilesPosition);
|
||||
|
||||
default boolean isDebugEnabled(){
|
||||
if(getCore() == null)
|
||||
if(core() == null)
|
||||
return false;
|
||||
|
||||
return getCore().getFileHandler().getBoolean("debug");
|
||||
return core().fileHandler().getBoolean("debug");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package ch.andre601.advancedserverlist.core.interfaces.dialogs;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.NullBool;
|
||||
import ch.andre601.advancedserverlist.core.commands.CmdProfiles;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IDialog<T>{
|
||||
T createDialog();
|
||||
|
||||
interface IDialogBuilder{
|
||||
IDialogBuilder appendBody(String body);
|
||||
|
||||
IDialogBuilder appendTextInput(CmdProfiles.ProfileOption option, String title, String initValue, int maxLength);
|
||||
|
||||
IDialogBuilder appendTextbox(CmdProfiles.ProfileOption option, String title, List<String> initValue, int maxLength, int rows);
|
||||
|
||||
IDialogBuilder appendNumberRange(CmdProfiles.ProfileOption option, String title, int initValue);
|
||||
|
||||
IDialogBuilder appendBoolean(CmdProfiles.ProfileOption option, String title, NullBool initValue);
|
||||
|
||||
IDialog<?> build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package ch.andre601.advancedserverlist.core.interfaces.dialogs;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
|
||||
public interface IDialogList{
|
||||
|
||||
void show(CmdSender sender);
|
||||
|
||||
interface IDialogListBuilder{
|
||||
IDialogListBuilder appendBody(String body);
|
||||
|
||||
IDialogListBuilder appendDialog(IDialog<?> dialog);
|
||||
|
||||
IDialogList build();
|
||||
}
|
||||
}
|
||||
@@ -40,19 +40,23 @@ public interface GenericEventWrapper<F, P extends GenericPlayer>{
|
||||
|
||||
GenericServerListEvent callEvent(ProfileEntry entry);
|
||||
|
||||
void setMaxPlayers(int maxPlayers);
|
||||
void maxPlayers(int maxPlayers);
|
||||
|
||||
void setMotd(Component component);
|
||||
void onlinePlayers(int onlinePlayers);
|
||||
|
||||
void motd(Component component);
|
||||
|
||||
void hidePlayers();
|
||||
|
||||
void setPlayerCount(String name);
|
||||
void playerCount(String name);
|
||||
|
||||
void setPlayers(List<String> players, P player, GenericServer server);
|
||||
void players(List<String> players, P player, GenericServer server);
|
||||
|
||||
void setFavicon(F favicon);
|
||||
void playersHidden();
|
||||
|
||||
void setDefaultFavicon();
|
||||
void favicon(F favicon);
|
||||
|
||||
void defaultFavicon();
|
||||
|
||||
void updateEvent();
|
||||
|
||||
@@ -60,23 +64,23 @@ public interface GenericEventWrapper<F, P extends GenericPlayer>{
|
||||
|
||||
boolean isMaintenanceModeActive();
|
||||
|
||||
int getProtocolVersion();
|
||||
int protocolVersion();
|
||||
|
||||
int getOnlinePlayers();
|
||||
int onlinePlayers();
|
||||
|
||||
int getMaxPlayers();
|
||||
int maxPlayers();
|
||||
|
||||
String getPlayerIP();
|
||||
String playerIP();
|
||||
|
||||
String parsePAPIPlaceholders(String text, P player);
|
||||
|
||||
String getVirtualHost();
|
||||
String virtualHost();
|
||||
|
||||
PluginCore<F> getPlugin();
|
||||
PluginCore<F> plugin();
|
||||
|
||||
P createPlayer(CachedPlayer player, int protocol);
|
||||
|
||||
GenericServer createGenericServer(int playersOnline, int playersMax, String host);
|
||||
GenericServer createServer(int playersOnline, int playersMax, String host);
|
||||
|
||||
default String resolveHost(InetSocketAddress address){
|
||||
return address == null ? null : address.getHostString();
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* 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){
|
||||
sender.sendMsg("Loading Configurate-Hocon library. This may take a while...");
|
||||
core.plugin().downloadLibrary("org.spongepowered", "configurate-hocon", "4.1.2");
|
||||
sender.sendMsg("<green>Library loaded!");
|
||||
|
||||
PluginLogger logger = core.plugin().pluginLogger();
|
||||
String folder = core.plugin().loader().equals("velocity") ? "minimotd-velocity" : "MiniMOTD";
|
||||
|
||||
Path mainConf = core.plugin().folderPath().getParent().resolve(folder).resolve("main.conf");
|
||||
if(!Files.exists(mainConf)){
|
||||
logger.warn("[<white>Migrator - MiniMOTD</white>] Cannot find a main.conf file in <white>/plugins/%s/</white> 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("[<white>Migrator - MiniMOTD</white>] 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("[<white>Migrator - MiniMOTD</white>] 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().getFirst());
|
||||
}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().getFirst();
|
||||
if(!motd.icon().isEmpty()){
|
||||
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()){
|
||||
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("[<white>Migrator - MiniMOTD</white>] Main ProfileEntry was invalid and profiles were empty.");
|
||||
sender.sendErrorMsg(" -> <red>Received invalid configuration.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Path profile = core.plugin().folderPath().resolve("profiles").resolve("minimotd_migrated.yml");
|
||||
if(Files.exists(profile)){
|
||||
logger.warn("[<white>Migrator - MiniMOTD</white>] 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("[<white>Migrator - MiniMOTD</white>] 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("[<white>Migrator - MiniMOTD</white>] 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("[<white>Migrator - MiniMOTD</white>] 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("[<white>Migrator - MiniMOTD</white>] 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("[<white>Migrator - MiniMOTD</white>] 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,16 +23,13 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.bungeecord.objects.impl;
|
||||
package ch.andre601.advancedserverlist.core.migration.minimotd.serializing;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.objects.CachedPlayer;
|
||||
import ch.andre601.advancedserverlist.core.profiles.players.GenericPlayerImpl;
|
||||
import java.util.List;
|
||||
|
||||
public class BungeePlayerImpl extends GenericPlayerImpl{
|
||||
public record MiniMOTDConfig(boolean iconEnabled, boolean motdEnabled, List<Motd> motds, PlayerCount playerCount){
|
||||
|
||||
public BungeePlayerImpl(CachedPlayer player, int protocol){
|
||||
this.name = player.name();
|
||||
this.protocol = protocol;
|
||||
this.uuid = player.uuid();
|
||||
}
|
||||
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,467 @@
|
||||
/*
|
||||
* 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}"
|
||||
);
|
||||
private static final Map<String, String> COLOR_TO_MINIMESSAGE_TAG = new HashMap<>(){{
|
||||
// Color Codes.
|
||||
put("a", "<green>");
|
||||
put("b", "<aqua>");
|
||||
put("c", "<red>");
|
||||
put("d", "<light_purple>");
|
||||
put("e", "<yellow>");
|
||||
put("f", "<white>");
|
||||
put("0", "<black>");
|
||||
put("1", "<dark_blue>");
|
||||
put("2", "<dark_green>");
|
||||
put("3", "<dark_aqua>");
|
||||
put("4", "<dark_red>");
|
||||
put("5", "<dark_purple>");
|
||||
put("6", "<gold>");
|
||||
put("7", "<grey>");
|
||||
put("8", "<dark_grey>");
|
||||
put("9", "<blue>");
|
||||
|
||||
// Formatting Codes
|
||||
put("k", "<obfuscated>");
|
||||
put("l", "<bold>");
|
||||
put("m", "<strikethrough>");
|
||||
put("n", "<underlined>");
|
||||
put("o", "<italic>");
|
||||
put("r", "<reset>");
|
||||
}};
|
||||
|
||||
public static int migrate(AdvancedServerList<?> core, CmdSender sender){
|
||||
ServerListPlusCore slpCore = ServerListPlusCore.getInstance();
|
||||
PluginLogger logger = core.plugin().pluginLogger();
|
||||
if(slpCore == null){
|
||||
logger.warn("[<white>Migrator - ServerListPlus</white>] Cannot migrate ServerListPlus config. ServerListPlus is not active!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ServerStatusConf conf = slpCore.getConf(ServerStatusConf.class);
|
||||
if(conf == null){
|
||||
logger.warn("[<white>Migrator - ServerListPlus</white>] 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.plugin().pluginLogger();
|
||||
if(conf == null){
|
||||
logger.info("[<white>Migrator - ServerListPlus</white>] No StatusConf found for type <white>%s</white>. 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("[<white>Migrator - ServerListPlus</white>] Unable to parse ServerListPlus configuration of type <white>%s</white>. Generated ProfileEntry was invalid.", type.getName());
|
||||
sender.sendErrorMsg(" -> <red>Received invalid Configuration.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Path profile = core.plugin().folderPath().resolve("profiles").resolve(type.getFile());
|
||||
if(Files.exists(profile)){
|
||||
logger.warn("[<white>Migrator - ServerListPlus</white>] Cannot create new file <white>%s</white>. 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("[<white>Migrator - ServerListPlus</white>] Encountered an IOException while trying to create file <white>%s</white>.", 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("[<white>Migrator - ServerListPlus</white>] Encountered an IOException while trying to load file <white>%s</white>.", ex, type.getFile());
|
||||
sender.sendErrorMsg(" -> <red>File loading error.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(node == null){
|
||||
logger.warn("[<white>Migrator - ServerListPlus</white>] Cannot migrate Configuration of type <white>%s</white>. 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.fileHandler().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("[<white>Migrator - ServerListPlus</white>] 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("[<white>Migrator - ServerListPlus</white>] Encountered an IOException while trying to save new file <white>%s</white>.", 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{
|
||||
for(Map.Entry<String, String> colorCodes : COLOR_TO_MINIMESSAGE_TAG.entrySet()){
|
||||
if(!colorCodeMatcher.group("color").equalsIgnoreCase(colorCodes.getKey()))
|
||||
continue;
|
||||
|
||||
colorCodeMatcher.appendReplacement(builder, colorCodes.getValue());
|
||||
}
|
||||
}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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,27 +23,30 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.compat.papi;
|
||||
package ch.andre601.advancedserverlist.core.objects;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PAPICache{
|
||||
public class ValueCache<T>{
|
||||
|
||||
private CacheValue cache = null;
|
||||
private final Duration duration;
|
||||
private CacheValue<T> cache = null;
|
||||
|
||||
public PAPICache(){}
|
||||
|
||||
public String get(Supplier<String> supplier){
|
||||
if(cache == null || cache.isExpired())
|
||||
cache = new CacheValue(supplier.get(), System.currentTimeMillis());
|
||||
|
||||
return cache.server();
|
||||
public ValueCache(Duration duration){
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
private record CacheValue(String server, long timestamp){
|
||||
// Consider the cache expired if it is older than 5 seconds.
|
||||
public boolean isExpired(){
|
||||
return (timestamp < 0L) || System.currentTimeMillis() - timestamp >= 5000L;
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,11 +25,13 @@
|
||||
|
||||
package ch.andre601.advancedserverlist.core.parsing;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.compat.miniplaceholders.MiniPlaceholdersUtil;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ComponentParser{
|
||||
@@ -52,12 +54,24 @@ public class ComponentParser{
|
||||
return new ComponentParser(text);
|
||||
}
|
||||
|
||||
public static ComponentParser list(List<String> lines){
|
||||
return new ComponentParser(String.join("\n", lines));
|
||||
public static ComponentParser textFormatted(String text, Object... args){
|
||||
return text(String.format(text, args));
|
||||
}
|
||||
|
||||
public ComponentParser modifyText(Function<String, String> function){
|
||||
this.text = function.apply(text);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ComponentParser applyMMTags(PluginCore<?> plugin){
|
||||
if(!plugin.isPluginEnabled("MiniPlaceholders"))
|
||||
return this;
|
||||
|
||||
TagResolver resolver = MiniPlaceholdersUtil.tagResolvers();
|
||||
Component component = mm.deserialize(this.text, resolver);
|
||||
|
||||
this.text = mm.serialize(component);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -65,8 +79,16 @@ 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());
|
||||
}
|
||||
|
||||
public String toStringStriped(){
|
||||
return mm.stripTags(text);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package ch.andre601.advancedserverlist.core.parsing;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import net.kyori.adventure.text.flattener.ComponentFlattener;
|
||||
import net.kyori.adventure.text.flattener.FlattenerListener;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
public class TextCenterUtil{
|
||||
private final ComponentFlattener COMPONENT_FLATTENER = ComponentFlattener.textOnly();
|
||||
|
||||
private final double MAX_WIDTH = 270.0; // Max width of the MOTD
|
||||
private final int SPACE_WIDTH = 4;
|
||||
|
||||
private final Map<String, FontInfo> charWidths;
|
||||
|
||||
public TextCenterUtil(AdvancedServerList<?> plugin){
|
||||
charWidths = loadFontWidths(plugin);
|
||||
}
|
||||
|
||||
public String getCenteredText(String text){
|
||||
if(!text.toLowerCase(Locale.ROOT).startsWith("<center>"))
|
||||
return text;
|
||||
|
||||
if(getDefault() == null)
|
||||
return text;
|
||||
|
||||
text = text.substring("<center>".length());
|
||||
TextFlattenerListener listener = new TextFlattenerListener(this);
|
||||
|
||||
COMPONENT_FLATTENER.flatten(ComponentParser.text(text).toComponent(), listener);
|
||||
|
||||
double width = listener.getWidth();
|
||||
if(width >= MAX_WIDTH)
|
||||
return text;
|
||||
|
||||
return createSpaces(width) + text;
|
||||
}
|
||||
|
||||
private double getCharWidth(int codePoint, boolean isBold, String fontName){
|
||||
FontInfo font = charWidths.getOrDefault(fontName, getDefault());
|
||||
if(font == null)
|
||||
return -1.0;
|
||||
|
||||
int index = Arrays.binarySearch(font.codePoints, codePoint);
|
||||
if(index < 0){
|
||||
return isBold ? 7.0 : 6.0;
|
||||
}else{
|
||||
return isBold ? font.advanceBf[index] : font.advance[index];
|
||||
}
|
||||
}
|
||||
|
||||
private FontInfo getDefault(){
|
||||
return charWidths.get("minecraft:default");
|
||||
}
|
||||
|
||||
private String createSpaces(double length){
|
||||
double remainder = MAX_WIDTH - length;
|
||||
int padding = (int)Math.max(0, Math.floor(remainder / 2.0) - 1);
|
||||
|
||||
return " ".repeat(Math.floorDiv(padding, SPACE_WIDTH));
|
||||
}
|
||||
|
||||
private Map<String, FontInfo> loadFontWidths(AdvancedServerList<?> plugin){
|
||||
try(InputStream stream = plugin.getClass().getResourceAsStream("/char-widths.json.gz")){
|
||||
if(stream == null){
|
||||
plugin.plugin().pluginLogger().warn("Internal char-widths.json.gz couldn't be retrieved.");
|
||||
plugin.plugin().pluginLogger().warn("The <center> Placeholder won't work.");
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
GZIPInputStream gzipStream = new GZIPInputStream(stream);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(gzipStream));
|
||||
|
||||
return new Gson().fromJson(reader, new TypeToken<Map<String, FontInfo>>(){}.getType());
|
||||
}catch(IOException ex){
|
||||
plugin.plugin().pluginLogger().warn("Encountered IOException while loading font widths.");
|
||||
plugin.plugin().pluginLogger().warn("The <center> Placeholder won't work.");
|
||||
plugin.plugin().pluginLogger().warn("Cause: <white>%s</white>", ex.getMessage());
|
||||
return new HashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
private static class TextFlattenerListener implements FlattenerListener{
|
||||
private double width = 0.0;
|
||||
private boolean isBold = false;
|
||||
private String font = "minecraft:default";
|
||||
|
||||
private final TextCenterUtil instance;
|
||||
|
||||
public TextFlattenerListener(TextCenterUtil instance){
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushStyle(@NotNull Style style){
|
||||
if(style.hasDecoration(TextDecoration.BOLD))
|
||||
isBold = true;
|
||||
|
||||
if(style.font() != null)
|
||||
font = style.font().asString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void component(@NotNull String text){
|
||||
for(int i = 0; i < text.length(); i++){
|
||||
width += instance.getCharWidth(text.codePointAt(i), isBold, font);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void popStyle(@NotNull Style style){
|
||||
if(style.hasDecoration(TextDecoration.BOLD))
|
||||
isBold = false;
|
||||
}
|
||||
|
||||
public double getWidth(){
|
||||
return width;
|
||||
}
|
||||
}
|
||||
|
||||
private record FontInfo(int[] codePoints, float[] advance, float[] advanceBf){}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package ch.andre601.advancedserverlist.core.placeholders;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class DatePlaceholder extends PlaceholderProvider{
|
||||
|
||||
public DatePlaceholder(){
|
||||
super("date");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String parsePlaceholder(@NonNull String placeholder, @NonNull GenericPlayer player, @NonNull GenericServer server){
|
||||
String[] args = placeholder.split("\\s+");
|
||||
|
||||
return switch(args[0]){
|
||||
case "current" -> {
|
||||
String format = "dd/MM/yyyy";
|
||||
if(args.length > 1){
|
||||
format = format(Arrays.copyOfRange(args, 1, args.length));
|
||||
}
|
||||
|
||||
try{
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat(format);
|
||||
yield dateFormat.format(new Date());
|
||||
}catch(IllegalArgumentException ex){
|
||||
yield "ERR: Invalid Date pattern " + format;
|
||||
}
|
||||
}
|
||||
case "isBefore" -> compareDates(args, integer -> integer < 0);
|
||||
case "isExactly" -> compareDates(args, integer -> integer == 0);
|
||||
case "isAfter" -> compareDates(args, integer -> integer > 0);
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private String format(String... args){
|
||||
if(args.length == 0)
|
||||
return "dd/MM/yyyy";
|
||||
|
||||
String format = String.join(" ", args);
|
||||
if(format.trim().isEmpty()){
|
||||
return "dd/MM/yyyy";
|
||||
}else{
|
||||
return format.trim();
|
||||
}
|
||||
}
|
||||
|
||||
private String compareDates(String[] args, Function<Integer, Boolean> function){
|
||||
if(args.length == 1)
|
||||
return "ERR: No Date provided!";
|
||||
|
||||
String targetDateStr = args[1];
|
||||
String format = "dd/MM/yyyy";
|
||||
if(args.length > 2){
|
||||
format = format(Arrays.copyOfRange(args, 2, args.length));
|
||||
}
|
||||
|
||||
SimpleDateFormat dateFormat;
|
||||
try{
|
||||
dateFormat = new SimpleDateFormat(format);
|
||||
Date targetDate = dateFormat.parse(targetDateStr);
|
||||
int diff = new Date().compareTo(targetDate);
|
||||
|
||||
return String.valueOf(function.apply(diff));
|
||||
}catch(IllegalArgumentException ex){
|
||||
return "ERR: Invalid Date format provided!";
|
||||
}catch(ParseException ex){
|
||||
return "ERR: Received invalid Date " + targetDateStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,10 +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.ProfileConditionParser;
|
||||
import ch.andre601.expressionparser.ParseWarnCollector;
|
||||
import ch.andre601.expressionparser.templates.ExpressionTemplate;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
|
||||
@@ -42,33 +41,36 @@ import java.util.Random;
|
||||
public record ServerListProfile(int priority, String condition, ProfileEntry defaultProfile, List<ProfileEntry> profiles, String file){
|
||||
private static final Random random = new Random();
|
||||
|
||||
public boolean evalConditions(ProfileConditionParser parser, PluginLogger logger, GenericPlayer player, GenericServer server){
|
||||
public boolean evalConditions(AdvancedServerList<?> core, GenericPlayer player, GenericServer server){
|
||||
if(condition == null || condition.isEmpty())
|
||||
return true;
|
||||
|
||||
ParseWarnCollector collector = new ParseWarnCollector(condition);
|
||||
ExpressionTemplate template = parser.compile(condition, player, server, collector);
|
||||
PluginLogger logger = core.plugin().pluginLogger();
|
||||
|
||||
boolean result;
|
||||
try{
|
||||
result = core.parser().compileToBoolean(condition, collector, player, server);
|
||||
}catch(Exception ex){
|
||||
logger.warn("Encountered an Exception while parsing condition '%s'. Cause: %s", condition, ex.getMessage());
|
||||
if(core.fileHandler().getBool(false, "debug"))
|
||||
logger.warn("Stacktrace:", ex);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if(collector.hasWarnings()){
|
||||
logger.warn("Encountered %d Error(s) while parsing condition for file '%s':", collector.getWarnings().size(), file);
|
||||
logger.warn("Encountered <white>%d</white> Error(s) while parsing condition for file '<white>%s</white>':", collector.getWarnings().size(), file);
|
||||
|
||||
for(ParseWarnCollector.Context context : collector.getWarnings()){
|
||||
if(context.position() <= -1){
|
||||
logger.warn(" - %s", condition);
|
||||
logger.warn(" -> %s", context.message());
|
||||
}else{
|
||||
logger.warn(" - At position %d:", context.position());
|
||||
logger.warn(" %s", condition);
|
||||
logger.warn(" ".repeat(context.position() + 5) + "^");
|
||||
logger.warn(" -> %s", context.message());
|
||||
}
|
||||
logger.warn(" - At position <white>%s</white>:", context.position() == -1 ? "<italic>Unknown</italic>" : context.position());
|
||||
logger.warn(" <white>%s</white>", condition);
|
||||
logger.warn(" ".repeat(context.position() + 3) + "<red>^</red>");
|
||||
logger.warn(" <grey>-></grey> %s", context.message());
|
||||
}
|
||||
}
|
||||
|
||||
if(template == null)
|
||||
return false;
|
||||
|
||||
return template.returnBooleanExpression().evaluate();
|
||||
return result;
|
||||
}
|
||||
|
||||
public ProfileEntry getRandomProfile(){
|
||||
@@ -77,7 +79,7 @@ public record ServerListProfile(int priority, String condition, ProfileEntry def
|
||||
}
|
||||
|
||||
if(profiles.size() == 1)
|
||||
return profiles.get(0); // No need to run a random for 1 profile.
|
||||
return profiles.getFirst(); // No need to run a random for 1 profile.
|
||||
|
||||
synchronized(random){
|
||||
return profiles.get(random.nextInt(profiles.size()));
|
||||
@@ -135,6 +137,9 @@ public record ServerListProfile(int priority, String condition, ProfileEntry def
|
||||
}
|
||||
|
||||
private Builder resolveCondition(){
|
||||
if(node == null)
|
||||
throw new IllegalStateException("ConfigurationNode was null!");
|
||||
|
||||
String condition = node.node("condition").getString();
|
||||
if(condition == null || condition.isEmpty())
|
||||
return this;
|
||||
@@ -144,20 +149,26 @@ public record ServerListProfile(int priority, String condition, ProfileEntry def
|
||||
}
|
||||
|
||||
private Builder resolveProfiles(){
|
||||
if(node == null)
|
||||
throw new IllegalStateException("ConfigurationNode was null!");
|
||||
|
||||
try{
|
||||
this.profiles = node.node("profiles").getList(ProfileEntry.class);
|
||||
}catch(SerializationException ex){
|
||||
logger.warn("Encountered a SerializationException while resolving the profiles entry for %s", ex, fileName);
|
||||
logger.warn("Encountered a SerializationException while resolving the profiles entry for <white>%s</white>", ex, fileName);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private Builder resolveDefaultProfile(){
|
||||
if(node == null)
|
||||
throw new IllegalStateException("ConfigurationNode was null!");
|
||||
|
||||
try{
|
||||
this.defaultProfile = node.get(ProfileEntry.class);
|
||||
}catch(SerializationException ex){
|
||||
logger.warn("Encountered a SerializationException while resolving the global profile for %s", ex, fileName);
|
||||
logger.warn("Encountered a SerializationException while resolving the global profile for <white>%s</white>", ex, fileName);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||