mirror of
https://codeberg.org/Andre601/AdvancedServerList.git
synced 2026-11-05 09:00:15 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
No files matched your search
@@ -0,0 +1,43 @@
|
||||
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-tiny
|
||||
steps:
|
||||
- name: "Checkout Source Repository"
|
||||
uses: https://code.forgejo.org/actions/checkout@v6
|
||||
with:
|
||||
token: "${{ secrets.BOT_TOKEN }}"
|
||||
- name: "Checkout Target Repository"
|
||||
run: "git clone -b pages https://codeberg.org/Andre601/asl-docs site"
|
||||
- name: "Setup Python 3.x"
|
||||
uses: https://code.forgejo.org/actions/setup-python@v6
|
||||
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 "ASL Wiki Builder"
|
||||
mv domains site/.domains
|
||||
cd site
|
||||
git remote set-url origin https://${{ secrets.BOT_TOKEN }}@codeberg.org/Andre601/asl-docs
|
||||
git add --all
|
||||
git commit -m "Update Wiki ($( env TZ=Europe/Zurich date +"%d.%m.%Y %Z" ))"
|
||||
git push
|
||||
@@ -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-tiny
|
||||
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@v6
|
||||
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@v6
|
||||
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
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 71 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
File renamed without changes.
@@ -4,21 +4,23 @@ when:
|
||||
|
||||
steps:
|
||||
- name: "Build Jar and Upload"
|
||||
image: maven:3.9.9-eclipse-temurin-21
|
||||
secrets:
|
||||
- modrinth_api_token
|
||||
- hangar_api_token
|
||||
- discord_webhook_url
|
||||
image: maven:3.9.14-eclipse-temurin-21
|
||||
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:
|
||||
- mvn clean install
|
||||
- cd version-uploader/target
|
||||
- mv VersionUploader.jar ../../
|
||||
- cd ../../
|
||||
- java -jar VersionUploader.jar --all
|
||||
- name: "Upload Jars to Codeber"
|
||||
- name: "Upload Jars to Codeberg"
|
||||
image: woodpeckerci/plugin-gitea-release
|
||||
settings:
|
||||
base_url: https://codeberg.org
|
||||
@@ -27,6 +29,7 @@ steps:
|
||||
- "paper/target/AdvancedServerList-*.jar"
|
||||
- "velocity/target/AdvancedServerList-*.jar"
|
||||
- "addons/banplugins/target/AdvancedServerList-*.jar"
|
||||
- "addons/luckperms/target/AdvancedServerList-*.jar"
|
||||
api_key:
|
||||
from_secret: cb_token
|
||||
- name: "Create Post on Mastodon"
|
||||
@@ -43,4 +46,19 @@ steps:
|
||||
Modrinth - https://modrinth.com/plugin/advancedserverlist
|
||||
Hangar - https://hangar.papermc.io/Andre_601/AdvancedServerList
|
||||
Codeberg - https://codeberg.org/Andre601/AdvancedServerList
|
||||
visibility: public
|
||||
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: english
|
||||
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
|
||||
File renamed without changes.
@@ -1,13 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
## [5.0.5](https://codeberg.org/Andre601/AdvancedServerList/releases/tag/v5.0.5) - 2024-10-10
|
||||
|
||||
### ❤️ Thanks to all contributors! ❤️
|
||||
|
||||
@Andre601, @Andre_601-Bot
|
||||
|
||||
### Misc
|
||||
|
||||
- Update dependency ch.qos.logback:logback-classic to v1.5.9 [[#354](https://codeberg.org/Andre601/AdvancedServerList/pulls/354)]
|
||||
- Improve PAPIProxyBridge support [[#353](https://codeberg.org/Andre601/AdvancedServerList/pulls/353)]
|
||||
- Add automatic release action [[#351](https://codeberg.org/Andre601/AdvancedServerList/pulls/351)]
|
||||
@@ -40,7 +40,7 @@ A Server List Profile is a single YAML file located in the plugin's `profiles` f
|
||||
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)*
|
||||
> *Read more about [Priority](https://asl.andre601.ch/profiles/options/priority) and [Conditions](https://asl.andre601.ch/profiles/options/condition)*
|
||||
|
||||
### Placeholders
|
||||
|
||||
@@ -63,7 +63,7 @@ AdvancedServerList soft-depends on the following plugins, meaning their inclusio
|
||||
|
||||
| Plugin | Supported Platforms |
|
||||
|-------------------------------------------------------------------------|----------------------------------------|
|
||||
| [PlaceholderAPI](https://hangar.papermc.io/HelpChat/PlaceholderAPI) | Paper |
|
||||
| [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 |
|
||||
@@ -86,9 +86,18 @@ Below can you find a collection of videos made for my plugin. If you made a vide
|
||||
<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>
|
||||
|
||||
|
||||
+5
-3
@@ -6,12 +6,14 @@ To ensure proper safety and support are only certain versions and Platforms of A
|
||||
|
||||
| Major Version | Supported |
|
||||
|---------------|-----------|
|
||||
| v4 | Yes |
|
||||
| v3 | Yes* |
|
||||
| v5 | Yes |
|
||||
| v4 | Yes* |
|
||||
| v3 | No** |
|
||||
| v2 | No |
|
||||
| v1 | No |
|
||||
|
||||
\*Only receives support but no updates or bug fixes.
|
||||
\*Only receives support but no updates or bug fixes.
|
||||
\*\*Support ended on December 31st, 2024.
|
||||
|
||||
## Supported Server/Proxy Versions
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -65,14 +65,14 @@
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.20-R0.2</version>
|
||||
<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.20-R0.2</version>
|
||||
<version>1.21-R0.4</version>
|
||||
<type>javadoc</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
@@ -85,7 +85,7 @@
|
||||
<dependency>
|
||||
<groupId>com.velocitypowered</groupId>
|
||||
<artifactId>velocity-api</artifactId>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
<version>3.4.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -99,13 +99,13 @@
|
||||
<dependency>
|
||||
<groupId>space.arim.libertybans</groupId>
|
||||
<artifactId>bans-api</artifactId>
|
||||
<version>1.1.0-RC1</version>
|
||||
<version>1.1.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.gitlab.ruany</groupId>
|
||||
<artifactId>LiteBansAPI</artifactId>
|
||||
<version>0.5.0</version>
|
||||
<version>0.6.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -117,19 +117,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.13.0</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+20
-20
@@ -35,26 +35,6 @@ 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())));
|
||||
@@ -134,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');
|
||||
}
|
||||
}
|
||||
@@ -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>1.21-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.velocitypowered</groupId>
|
||||
<artifactId>velocity-api</artifactId>
|
||||
<version>3.4.0</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>
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package ch.andre601.advancedserverlist.luckperms.bungee;
|
||||
|
||||
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 net.md_5.bungee.api.plugin.Plugin;
|
||||
|
||||
public class LuckPermsAddon extends Plugin{
|
||||
|
||||
@Override
|
||||
public void onEnable(){
|
||||
if(getProxy().getPluginManager().getPlugin("AdvancedServerList") == null){
|
||||
getLogger().warning("AdvancedServerList not found. This plugin is required.");
|
||||
return;
|
||||
}
|
||||
if(getProxy().getPluginManager().getPlugin("LuckPerms") == null){
|
||||
getLogger().warning("LuckPerms not found. This plugin is required.");
|
||||
return;
|
||||
}
|
||||
|
||||
LuckPerms luckPerms;
|
||||
try{
|
||||
luckPerms = LuckPermsProvider.get();
|
||||
}catch(IllegalStateException ex){
|
||||
getLogger().severe("Unable to obtain LuckPerms API instance! The Plugin won't work.");
|
||||
return;
|
||||
}
|
||||
|
||||
getLogger().info("Registering LuckPerms Placeholders...");
|
||||
try{
|
||||
AdvancedServerListAPI.get().addPlaceholderProvider(new LuckPermsPlaceholder(luckPerms));
|
||||
getLogger().info("Successfully registered Placeholder!");
|
||||
}catch(InvalidPlaceholderProviderException ex){
|
||||
getLogger().warning("Couldn't register Placeholder. The plugin won't work.");
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+66
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+53
@@ -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,10 @@
|
||||
name: 'ASLLuckPermsAddon'
|
||||
author: 'Andre_601'
|
||||
version: '${plugin.version}'
|
||||
description: 'LuckPerms Add-on for AdvancedServerList to add placeholders to use.'
|
||||
|
||||
softDepends:
|
||||
- AdvancedServerList
|
||||
- LuckPerms
|
||||
|
||||
main: 'ch.andre601.advancedserverlist.luckperms.bungee.LuckPermsAddon'
|
||||
@@ -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"
|
||||
}
|
||||
+66
-36
@@ -40,8 +40,9 @@
|
||||
<description>${plugin.description}</description>
|
||||
|
||||
<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>
|
||||
|
||||
<repositories>
|
||||
@@ -49,6 +50,10 @@
|
||||
<id>sonatype</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>repo-sayandevelopment</id>
|
||||
<url>https://repo.sayandev.org/releases/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
@@ -69,14 +74,14 @@
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.20-R0.2</version>
|
||||
<version>1.21-R0.3</version>
|
||||
<type>jar</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.20-R0.2</version>
|
||||
<version>1.21-R0.3</version>
|
||||
<type>javadoc</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
@@ -105,7 +110,7 @@
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bungeecord</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -121,7 +126,19 @@
|
||||
<dependency>
|
||||
<groupId>com.github.LeonMangler</groupId>
|
||||
<artifactId>PremiumVanishAPI</artifactId>
|
||||
<version>2.9.0-4</version>
|
||||
<version>2.9.18-2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.sayandev</groupId>
|
||||
<artifactId>sayanvanish-api</artifactId>
|
||||
<version>1.7.3</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.sayandev</groupId>
|
||||
<artifactId>sayanvanish-proxy-bungeecord</artifactId>
|
||||
<version>1.7.3</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -132,10 +149,16 @@
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.incendo</groupId>
|
||||
<artifactId>cloud-bungee</artifactId>
|
||||
<version>${dependency.cloud-bungee}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>AdvancedServerList-BungeeCord-${plugin.version}</finalName>
|
||||
<finalName>AdvancedServerList-BungeeCord-${plugin.version}${project.build.build_id}</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<filtering>true</filtering>
|
||||
@@ -143,44 +166,51 @@
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.bstats</pattern>
|
||||
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.bstats</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>net.byteflux.libby</pattern>
|
||||
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.libby</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
<version>3.6.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.bstats</pattern>
|
||||
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.bstats</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.alessiodp.libby</pattern>
|
||||
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.libby</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>ch.andre601.expressionparser</pattern>
|
||||
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.expressionparser</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.unascribed.flexver</pattern>
|
||||
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.flexver</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.incendo.cloud</pattern>
|
||||
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.cloud</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
+60
-27
@@ -25,16 +25,20 @@
|
||||
|
||||
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.commands.BungeeCommandManagerInterface;
|
||||
import ch.andre601.advancedserverlist.bungeecord.listeners.PlayerJoinEventListener;
|
||||
import ch.andre601.advancedserverlist.bungeecord.listeners.ProxyPingEventListener;
|
||||
import ch.andre601.advancedserverlist.bungeecord.logging.BungeeLogger;
|
||||
import ch.andre601.advancedserverlist.bungeecord.objects.placeholders.BungeePlayerPlaceholders;
|
||||
import ch.andre601.advancedserverlist.bungeecord.objects.placeholders.BungeeProxyPlaceholders;
|
||||
import ch.andre601.advancedserverlist.bungeecord.objects.placeholders.BungeeServerPlaceholders;
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
|
||||
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.handlers.FaviconHandler;
|
||||
import com.alessiodp.libby.BungeeLibraryManager;
|
||||
import com.alessiodp.libby.Library;
|
||||
@@ -47,6 +51,8 @@ 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 org.incendo.cloud.CommandManager;
|
||||
import org.sayandev.sayanvanish.bungeecord.api.SayanVanishBungeeAPI;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.nio.file.Path;
|
||||
@@ -55,6 +61,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class BungeeCordCore extends Plugin implements PluginCore<Favicon>{
|
||||
|
||||
@@ -66,17 +73,19 @@ public class BungeeCordCore extends Plugin implements PluginCore<Favicon>{
|
||||
private AdvancedServerList<Favicon> core;
|
||||
private FaviconHandler<Favicon> faviconHandler = null;
|
||||
private BungeeAudiences audiences = null;
|
||||
private BungeeCommandManagerInterface handler;
|
||||
|
||||
private BungeeLibraryManager libraryManager = null;
|
||||
|
||||
@Override
|
||||
public void onLoad(){
|
||||
logger.info("Loading libraries. This may take a while...");
|
||||
// Using getLogger() here, because PluginLogger requires MiniMessage, which isn't loaded at this stage.
|
||||
getLogger().info("Loading libraries. This may take a while...");
|
||||
successfulLoad = loadLibs();
|
||||
if(successfulLoad){
|
||||
logger.info("Library loading completed successfully!");
|
||||
getLogger().info("Library loading completed successfully!");
|
||||
}else{
|
||||
logger.warn("Library loading completed with errors!");
|
||||
getLogger().warning("Library loading completed with errors!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,36 +93,37 @@ public class BungeeCordCore extends Plugin implements PluginCore<Favicon>{
|
||||
public void onEnable(){
|
||||
if(!successfulLoad){
|
||||
logger.warn("There were issues during the plugins Loading-phase! Please see the console for possible reasons.");
|
||||
logger.warn("The plugin won't be enabled to avoid problems.");
|
||||
logger.warn("Aborting further enabling of the plugin to avoid errors.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.audiences = BungeeAudiences.create(this);
|
||||
this.core = AdvancedServerList.init(this,
|
||||
BungeePlayerPlaceholders.init(), BungeeServerPlaceholders.init(this), BungeeProxyPlaceholders.init(this));
|
||||
this.handler = new BungeeCommandManagerInterface(this);
|
||||
this.core = AdvancedServerList.init(
|
||||
this,
|
||||
BungeePlayerPlaceholders.init(),
|
||||
BungeeServerPlaceholders.init(this),
|
||||
BungeeProxyPlaceholders.init(this)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable(){
|
||||
core.disable();
|
||||
getProxy().getScheduler().cancel(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadCommands(){
|
||||
getProxy().getPluginManager().registerCommand(this, new CmdAdvancedServerList(this));
|
||||
audiences.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadEvents(){
|
||||
new JoinEvent(this);
|
||||
new PingEvent(this);
|
||||
new PlayerJoinEventListener(this);
|
||||
new ProxyPingEventListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMetrics(){
|
||||
new Metrics(this, 15585).addCustomChart(new SimplePie("profiles",
|
||||
() -> String.valueOf(core.getFileHandler().getProfiles().size())
|
||||
() -> String.valueOf(core.fileHandler().profiles().size())
|
||||
));
|
||||
|
||||
}
|
||||
@@ -154,22 +164,22 @@ public class BungeeCordCore extends Plugin implements PluginCore<Favicon>{
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdvancedServerList<Favicon> getCore(){
|
||||
public AdvancedServerList<Favicon> core(){
|
||||
return core;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getFolderPath(){
|
||||
public Path folderPath(){
|
||||
return getDataFolder().toPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginLogger getPluginLogger(){
|
||||
public PluginLogger pluginLogger(){
|
||||
return logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaviconHandler<Favicon> getFaviconHandler(){
|
||||
public FaviconHandler<Favicon> faviconHandler(){
|
||||
if(faviconHandler == null)
|
||||
faviconHandler = new FaviconHandler<>(core);
|
||||
|
||||
@@ -177,12 +187,17 @@ public class BungeeCordCore extends Plugin implements PluginCore<Favicon>{
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlatformInfo(){
|
||||
public CommandManager<CmdSender> commandManager(){
|
||||
return handler.commandHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String platformInfo(){
|
||||
return getProxy().getName() + " " + getProxy().getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLoader(){
|
||||
public String loader(){
|
||||
return "bungeecord";
|
||||
}
|
||||
|
||||
@@ -191,27 +206,45 @@ public class BungeeCordCore extends Plugin implements PluginCore<Favicon>{
|
||||
return getProxy().getPluginManager().getPlugin(plugin) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDialogsSupported(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Favicon createFavicon(BufferedImage image) throws IllegalArgumentException{
|
||||
return Favicon.create(image);
|
||||
}
|
||||
|
||||
public BungeeAudiences getAudiences(){
|
||||
@Override
|
||||
public IDialogList.IDialogListBuilder createDialogListBuilder(String title){
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IDialog.IDialogBuilder createDialogBuilder(String title, ServerListProfile profile, int profilesPosition){
|
||||
return null;
|
||||
}
|
||||
|
||||
public BungeeAudiences audiences(){
|
||||
return audiences;
|
||||
}
|
||||
|
||||
public int getOnlinePlayers(ServerInfo server){
|
||||
public int onlinePlayers(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);
|
||||
}else // Do the same if SayanVanish is enabled.
|
||||
if(getProxy().getPluginManager().getPlugin("SayanVanish") != null){
|
||||
players.removeIf(player -> SayanVanishBungeeAPI.getInstance().isVanished(player.getUniqueId()));
|
||||
}
|
||||
|
||||
return players.size();
|
||||
}
|
||||
|
||||
public Map<String, ServerPing> getFetchedServers(){
|
||||
public Map<String, ServerPing> fetchedServers(){
|
||||
return fetchedServers;
|
||||
}
|
||||
|
||||
@@ -226,7 +259,7 @@ public class BungeeCordCore extends Plugin implements PluginCore<Favicon>{
|
||||
|
||||
return true;
|
||||
}catch(Exception ex){
|
||||
logger.warn("Encountered an Exception while trying to load dependencies.", ex);
|
||||
getLogger().log(Level.WARNING, "Encountered an Exception while trying to load dependencies.", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+16
-5
@@ -27,8 +27,10 @@ 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.audience.Audience;
|
||||
import net.kyori.adventure.platform.bungeecord.BungeeAudiences;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
public class BungeeCmdSender implements CmdSender{
|
||||
|
||||
@@ -40,13 +42,22 @@ public class BungeeCmdSender implements CmdSender{
|
||||
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());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Audience audience(){
|
||||
return bungeeAudiences.sender(sender);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayer(){
|
||||
return sender instanceof ProxiedPlayer;
|
||||
}
|
||||
|
||||
public CommandSender sender(){
|
||||
return sender;
|
||||
}
|
||||
}
|
||||
+23
-9
@@ -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,27 +20,41 @@
|
||||
* 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 ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandManagerInterface;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.plugin.Command;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.SenderMapper;
|
||||
import org.incendo.cloud.bungee.BungeeCommandManager;
|
||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||
|
||||
public class CmdAdvancedServerList extends Command{
|
||||
public class BungeeCommandManagerInterface implements CommandManagerInterface<CommandSender>{
|
||||
|
||||
private final BungeeCordCore plugin;
|
||||
|
||||
public CmdAdvancedServerList(BungeeCordCore plugin){
|
||||
super("advancedserverlist", "advancedserverlist.command.asl", "asl");
|
||||
public BungeeCommandManagerInterface(BungeeCordCore plugin){
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args){
|
||||
plugin.getCore().getCommandHandler().handle(new BungeeCmdSender(sender, plugin.getAudiences()), args);
|
||||
public CommandManager<CmdSender> commandHandler(){
|
||||
return new BungeeCommandManager<>(
|
||||
plugin,
|
||||
ExecutionCoordinator.asyncCoordinator(),
|
||||
senderMapper()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SenderMapper<CommandSender, CmdSender> senderMapper(){
|
||||
return SenderMapper.create(
|
||||
cmdSender -> new BungeeCmdSender(cmdSender, plugin.audiences()),
|
||||
sender -> ((BungeeCmdSender)sender).sender()
|
||||
);
|
||||
}
|
||||
}
|
||||
+20
-15
@@ -75,17 +75,17 @@ public class BungeeEventWrapper implements GenericEventWrapper<Favicon, BungeePl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxPlayers(int maxPlayers){
|
||||
public void maxPlayers(int maxPlayers){
|
||||
ping.getPlayers().setMax(maxPlayers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnlinePlayers(int onlinePlayers){
|
||||
public void onlinePlayers(int onlinePlayers){
|
||||
ping.getPlayers().setOnline(onlinePlayers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMotd(Component component){
|
||||
public void motd(Component component){
|
||||
ping.setDescriptionComponent(new TextComponent(BungeeComponentSerializer.get().serialize(component)));
|
||||
}
|
||||
|
||||
@@ -95,13 +95,13 @@ public class BungeeEventWrapper implements GenericEventWrapper<Favicon, BungeePl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerCount(String name){
|
||||
public void playerCount(String name){
|
||||
protocol.setName(name);
|
||||
protocol.setProtocol(-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayers(List<String> lines, BungeePlayerImpl player, GenericServer server){
|
||||
public void players(List<String> lines, BungeePlayerImpl player, GenericServer server){
|
||||
ServerPing.PlayerInfo[] players = new ServerPing.PlayerInfo[lines.size()];
|
||||
|
||||
for(int i = 0; i < players.length; i++){
|
||||
@@ -119,12 +119,17 @@ public class BungeeEventWrapper implements GenericEventWrapper<Favicon, BungeePl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFavicon(Favicon favicon){
|
||||
public void playersHidden(){
|
||||
ping.getPlayers().setSample(new ServerPing.PlayerInfo[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void favicon(Favicon favicon){
|
||||
ping.setFavicon(favicon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultFavicon(){
|
||||
public void defaultFavicon(){
|
||||
ping.setFavicon(ping.getFaviconObject());
|
||||
}
|
||||
|
||||
@@ -148,22 +153,22 @@ public class BungeeEventWrapper implements GenericEventWrapper<Favicon, BungeePl
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion(){
|
||||
public int protocolVersion(){
|
||||
return event.getConnection().getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOnlinePlayers(){
|
||||
return plugin.getOnlinePlayers(null);
|
||||
public int onlinePlayers(){
|
||||
return plugin.onlinePlayers(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxPlayers(){
|
||||
public int maxPlayers(){
|
||||
return ping.getPlayers().getMax();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlayerIP(){
|
||||
public String playerIP(){
|
||||
return ((InetSocketAddress)event.getConnection().getSocketAddress()).getHostString();
|
||||
}
|
||||
|
||||
@@ -191,12 +196,12 @@ public class BungeeEventWrapper implements GenericEventWrapper<Favicon, BungeePl
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVirtualHost(){
|
||||
public String virtualHost(){
|
||||
return this.resolveHost(event.getConnection().getVirtualHost());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginCore<Favicon> getPlugin(){
|
||||
public PluginCore<Favicon> plugin(){
|
||||
return plugin;
|
||||
}
|
||||
|
||||
@@ -206,7 +211,7 @@ public class BungeeEventWrapper implements GenericEventWrapper<Favicon, BungeePl
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericServer createGenericServer(int playersOnline, int playersMax, String host){
|
||||
public GenericServer createServer(int playersOnline, int playersMax, String host){
|
||||
Map<String, ServerInfo> servers = plugin.getProxy().getServers();
|
||||
|
||||
return new BungeeProxyImpl(servers, playersOnline, playersMax, host);
|
||||
|
||||
+6
-6
@@ -34,10 +34,10 @@ import net.md_5.bungee.event.EventHandler;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
public class JoinEvent implements Listener{
|
||||
public class PlayerJoinEventListener implements Listener{
|
||||
private final BungeeCordCore plugin;
|
||||
|
||||
public JoinEvent(BungeeCordCore plugin){
|
||||
public PlayerJoinEventListener(BungeeCordCore plugin){
|
||||
this.plugin = plugin;
|
||||
plugin.getProxy().getPluginManager().registerListener(plugin, this);
|
||||
}
|
||||
@@ -47,15 +47,15 @@ public class JoinEvent implements Listener{
|
||||
InetSocketAddress address = (InetSocketAddress)event.getPlayer().getPendingConnection().getSocketAddress();
|
||||
ProxiedPlayer player = event.getPlayer();
|
||||
|
||||
plugin.getCore().getPlayerHandler().addPlayer(address.getHostString(), player.getName(), player.getUniqueId());
|
||||
plugin.core().playerHandler().addPlayer(address.getHostString(), player.getName(), player.getUniqueId());
|
||||
|
||||
if(player.hasPermission("advancedserverlist.admin") || player.hasPermission("advancedserverlist.updatecheck")){
|
||||
if(plugin.getAudiences() == null || plugin.getCore().getUpdateChecker() == null)
|
||||
if(plugin.audiences() == null || plugin.core().updateChecker() == null)
|
||||
return;
|
||||
|
||||
BungeeCmdSender sender = new BungeeCmdSender(player, plugin.getAudiences());
|
||||
BungeeCmdSender sender = new BungeeCmdSender(player, plugin.audiences());
|
||||
|
||||
plugin.getCore().getUpdateChecker().performCachedUpdateCheck(sender);
|
||||
plugin.core().updateChecker().performCachedUpdateCheck(sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -33,11 +33,11 @@ import net.md_5.bungee.api.event.ProxyPingEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
|
||||
public class PingEvent implements Listener{
|
||||
public class ProxyPingEventListener implements Listener{
|
||||
|
||||
private final BungeeCordCore plugin;
|
||||
|
||||
public PingEvent(BungeeCordCore plugin){
|
||||
public ProxyPingEventListener(BungeeCordCore plugin){
|
||||
this.plugin = plugin;
|
||||
plugin.getProxy().getPluginManager().registerListener(plugin, this);
|
||||
}
|
||||
+5
-4
@@ -27,6 +27,7 @@ package ch.andre601.advancedserverlist.bungeecord.logging;
|
||||
|
||||
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@@ -55,21 +56,21 @@ public class BungeeLogger implements PluginLogger{
|
||||
|
||||
@Override
|
||||
public void info(String msg, Object... args){
|
||||
logger.info(String.format(msg, args));
|
||||
logger.info(ComponentParser.textFormatted(msg, args).toStringStriped());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String msg, Object... args){
|
||||
logger.warning(String.format(msg, args));
|
||||
logger.warning(ComponentParser.textFormatted(msg, args).toStringStriped());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String msg, Throwable throwable){
|
||||
logger.log(Level.WARNING, msg, throwable);
|
||||
logger.log(Level.WARNING, ComponentParser.text(msg).toStringStriped(), throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String msg, Throwable throwable, Object... args){
|
||||
logger.log(Level.WARNING, String.format(msg, args), throwable);
|
||||
logger.log(Level.WARNING, ComponentParser.textFormatted(msg, args).toStringStriped(), throwable);
|
||||
}
|
||||
}
|
||||
+5
-13
@@ -27,24 +27,16 @@ package ch.andre601.advancedserverlist.bungeecord.objects.impl;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.bungeecord.objects.BungeePlayer;
|
||||
import ch.andre601.advancedserverlist.core.objects.CachedPlayer;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class BungeePlayerImpl implements BungeePlayer{
|
||||
|
||||
private final String name;
|
||||
private final int protocol;
|
||||
private final UUID uuid;
|
||||
|
||||
public BungeePlayerImpl(CachedPlayer player, int protocol){
|
||||
this.name = player.name();
|
||||
this.protocol = protocol;
|
||||
this.uuid = player.uuid();
|
||||
}
|
||||
@NullMarked
|
||||
public record BungeePlayerImpl(CachedPlayer player, int protocol) implements BungeePlayer{
|
||||
|
||||
@Override
|
||||
public String getName(){
|
||||
return name;
|
||||
return player.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,6 +46,6 @@ public class BungeePlayerImpl implements BungeePlayer{
|
||||
|
||||
@Override
|
||||
public UUID getUUID(){
|
||||
return uuid;
|
||||
return player.uuid();
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -27,12 +27,13 @@ package ch.andre601.advancedserverlist.bungeecord.objects.impl;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.bungeecord.objects.BungeeProxy;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
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(){
|
||||
public @NonNull Map<String, ServerInfo> getServers(){
|
||||
return servers;
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -28,9 +28,12 @@ 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 org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@NullMarked
|
||||
public class BungeePlayerPlaceholders extends PlaceholderProvider{
|
||||
|
||||
private BungeePlayerPlaceholders(){
|
||||
@@ -42,7 +45,7 @@ public class BungeePlayerPlaceholders extends PlaceholderProvider{
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
|
||||
public @Nullable String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
|
||||
return switch(placeholder.toLowerCase(Locale.ROOT)){
|
||||
case "name" -> player.getName();
|
||||
case "protocol" -> String.valueOf(player.getProtocol());
|
||||
|
||||
+5
-2
@@ -9,7 +9,10 @@ import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||
import net.md_5.bungee.api.ServerPing;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@NullMarked
|
||||
public class BungeeProxyPlaceholders extends PlaceholderProvider{
|
||||
|
||||
private final BungeeCordCore plugin;
|
||||
@@ -25,12 +28,12 @@ public class BungeeProxyPlaceholders extends PlaceholderProvider{
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
|
||||
public @Nullable String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
|
||||
String[] args = placeholder.split("\\s", 2);
|
||||
if(args.length < 2)
|
||||
return null;
|
||||
|
||||
ServerPing ping = plugin.getFetchedServers().get(args[1]);
|
||||
ServerPing ping = plugin.fetchedServers().get(args[1]);
|
||||
|
||||
return switch(args[0]){
|
||||
case "status" -> ping == null ? "offline" : "online";
|
||||
|
||||
+6
-3
@@ -31,9 +31,12 @@ 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 org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
@NullMarked
|
||||
public class BungeeServerPlaceholders extends PlaceholderProvider{
|
||||
|
||||
private final BungeeCordCore plugin;
|
||||
@@ -48,7 +51,7 @@ public class BungeeServerPlaceholders extends PlaceholderProvider{
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
|
||||
public @Nullable String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
|
||||
if(!(server instanceof BungeeProxy proxy))
|
||||
return null;
|
||||
|
||||
@@ -68,7 +71,7 @@ public class BungeeServerPlaceholders extends PlaceholderProvider{
|
||||
if(info == null)
|
||||
continue;
|
||||
|
||||
players += plugin.getOnlinePlayers(info);
|
||||
players += plugin.onlinePlayers(info);
|
||||
}
|
||||
|
||||
yield String.valueOf(players);
|
||||
@@ -87,7 +90,7 @@ public class BungeeServerPlaceholders extends PlaceholderProvider{
|
||||
if(args.length > 2)
|
||||
yield "";
|
||||
|
||||
if(args.length == 2 && args[1] != null && !args[1].isEmpty()){
|
||||
if(args.length == 2 && !args[1].isEmpty()){
|
||||
ServerInfo info = proxy.getServers().get(args[1]);
|
||||
if(info == null)
|
||||
yield "";
|
||||
|
||||
+61
-45
@@ -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>6</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>
|
||||
@@ -77,9 +79,11 @@
|
||||
<dependency>
|
||||
<groupId>ch.andre601</groupId>
|
||||
<artifactId>expressionparser</artifactId>
|
||||
<version>1.4.3</version>
|
||||
<version>1.6.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Configuration -->
|
||||
<dependency>
|
||||
<groupId>org.spongepowered</groupId>
|
||||
<artifactId>configurate-yaml</artifactId>
|
||||
@@ -89,7 +93,7 @@
|
||||
<dependency>
|
||||
<groupId>org.spongepowered</groupId>
|
||||
<artifactId>configurate-hocon</artifactId>
|
||||
<version>4.1.2</version>
|
||||
<version>4.2.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -117,7 +121,7 @@
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.11.0</version>
|
||||
<version>2.13.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -129,15 +133,42 @@
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>33.3.1-jre</version>
|
||||
<version>33.5.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.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Plugins -->
|
||||
<dependency>
|
||||
<groupId>net.william278</groupId>
|
||||
<artifactId>papiproxybridge</artifactId>
|
||||
<version>1.6.2</version>
|
||||
<version>1.8.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -152,65 +183,50 @@
|
||||
<version>3.5.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.miniplaceholders</groupId>
|
||||
<artifactId>miniplaceholders-api</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Test dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.11.2</version>
|
||||
<version>5.14.3</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.13.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<version>3.15.0</version>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</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>
|
||||
|
||||
@@ -29,24 +29,51 @@ 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.CommandTypePostprocessor;
|
||||
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.parsing.TextCenterUtil;
|
||||
import ch.andre601.advancedserverlist.core.placeholders.DatePlaceholder;
|
||||
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.ProfileConditionParser;
|
||||
import ch.andre601.advancedserverlist.core.profiles.handlers.PlayerHandler;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.annotations.AnnotationParser;
|
||||
import org.incendo.cloud.description.Description;
|
||||
import org.incendo.cloud.injection.ParameterInjector;
|
||||
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();
|
||||
|
||||
@@ -57,69 +84,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);
|
||||
|
||||
plugin.getPluginLogger().info("Registering internal Placeholders...");
|
||||
plugin.pluginLogger().info("Registering internal Placeholders...");
|
||||
|
||||
placeholders.forEach(this::addPlaceholder);
|
||||
|
||||
if(plugin.isPluginEnabled("Maintenance")){
|
||||
plugin.getPluginLogger().info("Found Maintenance Plugin. Registering extra Placeholder...");
|
||||
addPlaceholder(new MaintenancePlaceholder(this));
|
||||
}
|
||||
|
||||
plugin.getPluginLogger().info("Completed internal Placeholder registration!");
|
||||
|
||||
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 static AdvancedServerListAPI getApi(){
|
||||
public static AdvancedServerListAPI api(){
|
||||
return api;
|
||||
}
|
||||
|
||||
public PluginCore<F> getPlugin(){
|
||||
public PluginCore<F> plugin(){
|
||||
return plugin;
|
||||
}
|
||||
|
||||
public FileHandler getFileHandler(){
|
||||
public FileHandler fileHandler(){
|
||||
return fileHandler;
|
||||
}
|
||||
|
||||
public CommandHandler getCommandHandler(){
|
||||
return commandHandler;
|
||||
}
|
||||
|
||||
public PlayerHandler getPlayerHandler(){
|
||||
public PlayerHandler playerHandler(){
|
||||
return playerHandler;
|
||||
}
|
||||
|
||||
public String getVersion(){
|
||||
public String version(){
|
||||
return version;
|
||||
}
|
||||
|
||||
public ProfileConditionParser getParser(){
|
||||
public ProfileConditionParser parser(){
|
||||
return parser;
|
||||
}
|
||||
|
||||
public UpdateChecker getUpdateChecker(){
|
||||
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(){
|
||||
@@ -127,82 +149,69 @@ 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: %s", plugin.getPlatformInfo());
|
||||
|
||||
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...");
|
||||
loadCommands();
|
||||
|
||||
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/");
|
||||
playerHandler().load();
|
||||
plugin.loadMetrics();
|
||||
getPlugin().getPluginLogger().info("Metrics loaded!");
|
||||
|
||||
getPlugin().getPluginLogger().info("Enabling Schedulers...");
|
||||
plugin.startScheduler();
|
||||
getPlugin().getPluginLogger().info("Schedulers enabled.");
|
||||
|
||||
getPlugin().getPluginLogger().info("AdvancedServerList is ready!");
|
||||
plugin().pluginLogger().success("<green>AdvancedServerList is ready!");
|
||||
|
||||
if(getFileHandler().getBoolean("checkUpdates"))
|
||||
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){
|
||||
@@ -211,7 +220,119 @@ public class AdvancedServerList<F>{
|
||||
}
|
||||
|
||||
private void addPlaceholder(PlaceholderProvider placeholderProvider){
|
||||
getPlugin().getPluginLogger().info("Registering %s Placeholders...", placeholderProvider.getIdentifier());
|
||||
AdvancedServerList.getApi().addPlaceholderProvider(placeholderProvider);
|
||||
AdvancedServerList.api().addPlaceholderProvider(placeholderProvider);
|
||||
}
|
||||
|
||||
private void loadCommands(){
|
||||
plugin().loadFaviconHandler(this);
|
||||
|
||||
CommandManager<CmdSender> commandManager = plugin().commandManager();
|
||||
|
||||
commandManager.parameterInjectorRegistry().registerInjector(AdvancedServerList.class, ParameterInjector.constantInjector(this));
|
||||
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(new CommandHandler(commandManager));
|
||||
|
||||
for(String option : CommandHandler.PROFILE_OPTIONS){
|
||||
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("");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,8 +68,8 @@ public class UpdateChecker{
|
||||
|
||||
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();
|
||||
@@ -88,10 +88,10 @@ public class UpdateChecker{
|
||||
if(version == null)
|
||||
return;
|
||||
|
||||
int result = version.compare(core.getVersion());
|
||||
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.getVersion());
|
||||
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'>" +
|
||||
@@ -101,8 +101,12 @@ public class UpdateChecker{
|
||||
);
|
||||
}else
|
||||
if(result == 1){
|
||||
sender.sendPrefixedMsg("You seem to run a newer Version (<white>%</white>) than what is available on Modrinth (<white>%s</white>).", core.getVersion(), version.versionNumber());
|
||||
sender.sendPrefixedMsg("Are you running a development build?");
|
||||
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?");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -123,24 +127,28 @@ 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);
|
||||
@@ -155,7 +163,7 @@ public class UpdateChecker{
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -165,7 +173,7 @@ public class UpdateChecker{
|
||||
request = HttpRequest.newBuilder()
|
||||
.uri(new URI(finalUrl))
|
||||
.timeout(Duration.ofSeconds(5))
|
||||
.header("User-Agent", "AdvancedServerList-" + loader + "/" + core.getVersion())
|
||||
.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);
|
||||
@@ -185,18 +193,18 @@ public class UpdateChecker{
|
||||
logger.warn("=======================================================================");
|
||||
logger.warn("You are running an outdated version of AdvancedServerList!");
|
||||
logger.warn("");
|
||||
logger.warn("Your version: %s", core.getVersion());
|
||||
logger.warn("Modrinth: %s", version.versionNumber());
|
||||
logger.warn("Your version: <white>%s</white>", core.version());
|
||||
logger.warn("Modrinth: <white>%s</white>", version.versionNumber());
|
||||
logger.warn("");
|
||||
|
||||
if(!version.isRelease()){
|
||||
logger.warn("WARNING!");
|
||||
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.warn("Download the latest version from here:");
|
||||
logger.warn("https://modrinth.com/plugin/advancedserverlist/version/%s", version.id());
|
||||
logger.warn("<white>https://modrinth.com/plugin/advancedserverlist/version/%s</white>", version.id());
|
||||
logger.warn("=======================================================================");
|
||||
}
|
||||
|
||||
|
||||
+871
-372
File diff suppressed because it is too large.
Load diff
+22
@@ -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();
|
||||
}
|
||||
}
|
||||
+172
@@ -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 String 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(String 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 && !option.isEmpty() && file != null && !file.isEmpty()){
|
||||
builder.append("<click:suggest_command:/asl profiles set ")
|
||||
.append(file)
|
||||
.append(" ")
|
||||
.append(option)
|
||||
.append(" >");
|
||||
}
|
||||
|
||||
builder.append(displayName);
|
||||
|
||||
if(option != null && !option.isEmpty() && 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-2
@@ -4,7 +4,10 @@ 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;
|
||||
@@ -15,8 +18,8 @@ public class MaintenancePlaceholder extends PlaceholderProvider{
|
||||
this.plugin = plugin;
|
||||
}
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
|
||||
if(!placeholder.equals("maintenanceEnabled") || !plugin.getPlugin().isPluginEnabled("Maintenance"))
|
||||
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());
|
||||
|
||||
+11
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ public class PAPIUtil{
|
||||
private static PAPIUtil instance;
|
||||
|
||||
private final ValueCache<CompletableFuture<Set<String>>> serversCache = new ValueCache<>(Duration.ofSeconds(10));
|
||||
private final Cache<UUID, CompletableFuture<String>> textCache = CacheBuilder.newBuilder()
|
||||
private final Cache<CacheKey, CompletableFuture<String>> textCache = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(Duration.ofSeconds(10))
|
||||
.build();
|
||||
private final PlaceholderAPI papi;
|
||||
@@ -72,21 +72,23 @@ public class PAPIUtil{
|
||||
if(values.isEmpty())
|
||||
return null;
|
||||
|
||||
return List.copyOf(values).get(0);
|
||||
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{
|
||||
return textCache.get(player, () -> papi.formatPlaceholders(text, carrier, player)).getNow(text);
|
||||
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){}
|
||||
}
|
||||
+51
-29
@@ -36,32 +36,36 @@ 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{
|
||||
|
||||
public static <F, P extends GenericPlayer> ProfileEntry handleEvent(GenericEventWrapper<F, P> event){
|
||||
event.getPlugin().getPluginLogger().debug(PingEventHandler.class, "Received ping event. Handling it...");
|
||||
event.plugin().pluginLogger().debug(PingEventHandler.class, "Received ping event. Handling it...");
|
||||
if(event.isInvalidProtocol()){
|
||||
event.getPlugin().getPluginLogger().debug(PingEventHandler.class, "Not handling event. Protocol was invalid.");
|
||||
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...");
|
||||
@@ -89,11 +93,12 @@ public class PingEventHandler{
|
||||
String onlinePlayers = ComponentParser.text(entry.onlinePlayersCount())
|
||||
.modifyText(text -> StringReplacer.replace(text, player, server))
|
||||
.modifyText(text -> event.parsePAPIPlaceholders(text, player))
|
||||
.applyMMTags(event.plugin())
|
||||
.toString();
|
||||
|
||||
try{
|
||||
online = onlinePlayers == null ? online : Integer.parseInt(onlinePlayers);
|
||||
event.setOnlinePlayers(online);
|
||||
event.onlinePlayers(online);
|
||||
}catch(NumberFormatException ex){
|
||||
logger.warn("Option 'playerCount -> onlinePlayers -> amount' is not a valid Number!");
|
||||
}
|
||||
@@ -106,13 +111,14 @@ public class PingEventHandler{
|
||||
String extraPlayers = ComponentParser.text(entry.extraPlayersCount())
|
||||
.modifyText(text -> StringReplacer.replace(text, player, server))
|
||||
.modifyText(text -> event.parsePAPIPlaceholders(text, player))
|
||||
.applyMMTags(event.plugin())
|
||||
.toString();
|
||||
|
||||
try{
|
||||
max = online + (extraPlayers == null ? 0 : Integer.parseInt(extraPlayers));
|
||||
|
||||
logger.debug(PingEventHandler.class, "'playerCount -> extraPlayers -> amount' is a valid number. Applying it...");
|
||||
event.setMaxPlayers(max);
|
||||
event.maxPlayers(max);
|
||||
}catch(NumberFormatException ex){
|
||||
logger.warn("Option 'playerCount -> extraPlayers -> amount' is not a valid Number!");
|
||||
}
|
||||
@@ -123,34 +129,40 @@ public class PingEventHandler{
|
||||
String maxPlayers = ComponentParser.text(entry.maxPlayersCount())
|
||||
.modifyText(text -> StringReplacer.replace(text, player, server))
|
||||
.modifyText(text -> event.parsePAPIPlaceholders(text, player))
|
||||
.applyMMTags(event.plugin())
|
||||
.toString();
|
||||
|
||||
try{
|
||||
max = maxPlayers == null ? 0 : Integer.parseInt(maxPlayers);
|
||||
|
||||
logger.debug(PingEventHandler.class, "'playerCount -> maxPlayers -> amount' is a valid number. Applying it...");
|
||||
event.setMaxPlayers(max);
|
||||
event.maxPlayers(max);
|
||||
}catch(NumberFormatException ex){
|
||||
logger.warn("Option 'playerCount -> maxPlayers -> amount' is not a valid Number!");
|
||||
}
|
||||
}
|
||||
|
||||
GenericServer finalServer = event.createGenericServer(online, max, host);
|
||||
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("\\n", entry.motd()));
|
||||
logger.debug(PingEventHandler.class, "'motd' option present. Applying ['%s']...", String.join("', '", entry.motd()));
|
||||
|
||||
event.setMotd(
|
||||
ComponentParser.list(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 && ignoreMaintenance(event, "hidePlayers")){
|
||||
if(hidePlayers && ignoreMaintenance(event, "hidePlayers") && ignoreMaintenance(event, "hidePlayersHover")){
|
||||
logger.debug(PingEventHandler.class, "'playerCount -> hidePlayers' enabled. Hiding player count...");
|
||||
|
||||
event.hidePlayers();
|
||||
@@ -159,35 +171,45 @@ public class PingEventHandler{
|
||||
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 && ignoreMaintenance(event, "playerCountHover")){
|
||||
logger.debug(PingEventHandler.class, "'playerCount -> hover' option set. Applying '%s'...", String.join("\\n", entry.players()));
|
||||
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.players()) && !hidePlayers && !hidePlayersHover && ignoreMaintenance(event, "playerCountHover")){
|
||||
logger.debug(PingEventHandler.class, "'playerCount -> hover' option set. Applying ['%s']...", String.join("', '", entry.players()));
|
||||
|
||||
event.setPlayers(entry.players(), 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());
|
||||
|
||||
String faviconString = StringReplacer.replace(entry.favicon(), player, server);
|
||||
String faviconString = StringReplacer.modifier(entry.favicon())
|
||||
.modify(text -> StringReplacer.replace(text, player, server))
|
||||
.modify(text -> event.parsePAPIPlaceholders(text, player))
|
||||
.asString();
|
||||
|
||||
F favicon = plugin.getFaviconHandler().getFavicon(faviconString);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,6 +223,6 @@ public class PingEventHandler{
|
||||
if(!event.isMaintenanceModeActive())
|
||||
return true;
|
||||
|
||||
return !event.getPlugin().getCore().getFileHandler().getBool(true, "disableDuringMaintenance", option);
|
||||
return !event.plugin().core().fileHandler().getBool(true, "disableDuringMaintenance", option);
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,11 @@ public class ConfigMigrator{
|
||||
public static ConfigurationTransformation.Versioned create(){
|
||||
return ConfigurationTransformation.versionedBuilder()
|
||||
.versionKey("configVersion")
|
||||
.addVersion(LATEST, fiveToSix())
|
||||
.addVersion(LATEST, nineToTen())
|
||||
.addVersion(9, eightToNine())
|
||||
.addVersion(8, sevenToEight())
|
||||
.addVersion(7, sixToSeven())
|
||||
.addVersion(6, fiveToSix())
|
||||
.addVersion(5, fourToFive())
|
||||
.addVersion(4, threeToFour())
|
||||
.addVersion(3, twoToThree())
|
||||
@@ -54,6 +58,42 @@ public class ConfigMigrator{
|
||||
.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) -> {
|
||||
@@ -122,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,7 +108,7 @@ public class FileHandler{
|
||||
}
|
||||
|
||||
public boolean loadProfiles(){
|
||||
logger.info("Loading profiles...");
|
||||
logger.info("[-] Loading profiles...");
|
||||
|
||||
if(createFile("default.yml")){
|
||||
return reloadProfiles();
|
||||
@@ -107,9 +117,51 @@ public class FileHandler{
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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.info("Successfully created profiles folder.");
|
||||
logger.debugSuccess(FileHandler.class, "Successfully created profiles folder.");
|
||||
}
|
||||
|
||||
File file = new File(profilesFolder.toFile(), name);
|
||||
@@ -126,7 +178,7 @@ public class FileHandler{
|
||||
Files.copy(stream, profilesFolder.resolve(name));
|
||||
return true;
|
||||
}catch(IOException ex){
|
||||
logger.warn("Encountered IOException while trying to create file %s", ex, name);
|
||||
logger.warn("Encountered IOException while trying to create file <white>%s</white>", ex, name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -160,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;
|
||||
}
|
||||
|
||||
@@ -170,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;
|
||||
}
|
||||
|
||||
@@ -191,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;
|
||||
}
|
||||
}
|
||||
@@ -218,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;
|
||||
}
|
||||
|
||||
@@ -231,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);
|
||||
}
|
||||
}
|
||||
+8
-4
@@ -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();
|
||||
}
|
||||
+7
-16
@@ -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,24 +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.velocity.commands;
|
||||
package ch.andre601.advancedserverlist.core.interfaces.commands;
|
||||
|
||||
import ch.andre601.advancedserverlist.velocity.VelocityCore;
|
||||
import com.velocitypowered.api.command.SimpleCommand;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.SenderMapper;
|
||||
|
||||
public class CmdAdvancedServerList implements SimpleCommand{
|
||||
public interface CommandManagerInterface<S>{
|
||||
CommandManager<CmdSender> commandHandler();
|
||||
|
||||
private final VelocityCore plugin;
|
||||
|
||||
public CmdAdvancedServerList(VelocityCore plugin){
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Invocation invocation){
|
||||
plugin.getCore().getCommandHandler().handle(new VelocityCmdSender(invocation.source()), invocation.arguments());
|
||||
}
|
||||
SenderMapper<S, CmdSender> senderMapper();
|
||||
}
|
||||
+12
-22
@@ -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,32 +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 name;
|
||||
|
||||
public PluginCommand(String name){
|
||||
this.name = name;
|
||||
enum Type{
|
||||
ALL,
|
||||
PLAYER_ONLY
|
||||
}
|
||||
|
||||
public String name(){
|
||||
return name;
|
||||
}
|
||||
|
||||
public String permission(){
|
||||
return "advancedserverlist.command." + name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
public abstract void handle(CmdSender sender, String[] args);
|
||||
|
||||
public abstract String usage();
|
||||
|
||||
public abstract String description();
|
||||
}
|
||||
+21
-10
@@ -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.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();
|
||||
@@ -48,26 +51,34 @@ public interface PluginCore<F>{
|
||||
|
||||
void startScheduler();
|
||||
|
||||
AdvancedServerList<F> getCore();
|
||||
AdvancedServerList<F> core();
|
||||
|
||||
Path getFolderPath();
|
||||
Path folderPath();
|
||||
|
||||
PluginLogger getPluginLogger();
|
||||
PluginLogger pluginLogger();
|
||||
|
||||
FaviconHandler<F> getFaviconHandler();
|
||||
FaviconHandler<F> faviconHandler();
|
||||
|
||||
String getPlatformInfo();
|
||||
CommandManager<CmdSender> commandManager();
|
||||
|
||||
String getLoader();
|
||||
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");
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package ch.andre601.advancedserverlist.core.interfaces.dialogs;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.NullBool;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IDialog<T>{
|
||||
T createDialog();
|
||||
|
||||
interface IDialogBuilder{
|
||||
IDialogBuilder appendBody(String body);
|
||||
|
||||
IDialogBuilder appendTextInput(String key, String title, String initValue, int maxLength);
|
||||
|
||||
IDialogBuilder appendTextbox(String key, String title, List<String> initValue, int maxLength, int rows);
|
||||
|
||||
IDialogBuilder appendNumberRange(String key, String title, int initValue);
|
||||
|
||||
IDialogBuilder appendBoolean(String key, String title, NullBool initValue);
|
||||
|
||||
IDialog<?> build();
|
||||
}
|
||||
}
|
||||
+16
@@ -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();
|
||||
}
|
||||
}
|
||||
+16
-14
@@ -40,21 +40,23 @@ public interface GenericEventWrapper<F, P extends GenericPlayer>{
|
||||
|
||||
GenericServerListEvent callEvent(ProfileEntry entry);
|
||||
|
||||
void setMaxPlayers(int maxPlayers);
|
||||
void maxPlayers(int maxPlayers);
|
||||
|
||||
void setOnlinePlayers(int onlinePlayers);
|
||||
void onlinePlayers(int onlinePlayers);
|
||||
|
||||
void setMotd(Component component);
|
||||
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();
|
||||
|
||||
@@ -62,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();
|
||||
|
||||
+21
-19
@@ -49,14 +49,16 @@ import java.util.List;
|
||||
public class MiniMOTDConfigMigrator{
|
||||
|
||||
public static boolean migrate(AdvancedServerList<?> core, CmdSender sender){
|
||||
core.getPlugin().downloadLibrary("org.spongepowered", "configurate-hocon", "4.1.2");
|
||||
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.getPlugin().getPluginLogger();
|
||||
String folder = core.getPlugin().getLoader().equals("velocity") ? "minimotd-velocity" : "MiniMOTD";
|
||||
PluginLogger logger = core.plugin().pluginLogger();
|
||||
String folder = core.plugin().loader().equals("velocity") ? "minimotd-velocity" : "MiniMOTD";
|
||||
|
||||
Path mainConf = core.getPlugin().getFolderPath().getParent().resolve(folder).resolve("main.conf");
|
||||
Path mainConf = core.plugin().folderPath().getParent().resolve(folder).resolve("main.conf");
|
||||
if(!Files.exists(mainConf)){
|
||||
logger.warn("[Migrator - MiniMOTD] Cannot find a main.conf file in /plugins/%s/ folder.", folder);
|
||||
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;
|
||||
}
|
||||
@@ -76,13 +78,13 @@ public class MiniMOTDConfigMigrator{
|
||||
|
||||
config = node.get(MiniMOTDConfig.class);
|
||||
}catch(IOException ex){
|
||||
logger.warn("[Migrator - MiniMOTD] Encountered IOException while loading main.conf file.", 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("[Migrator - MiniMOTD] Retrieved main.conf file returned 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;
|
||||
}
|
||||
@@ -98,7 +100,7 @@ public class MiniMOTDConfigMigrator{
|
||||
|
||||
if(config.motdEnabled()){
|
||||
if(config.motds().size() == 1){
|
||||
applyMotd(profileBuilder, config.motds().get(0));
|
||||
applyMotd(profileBuilder, config.motds().getFirst());
|
||||
}else
|
||||
if(config.motds().size() > 1){
|
||||
for(int i = 0; i < config.motds().size(); i++){
|
||||
@@ -109,15 +111,15 @@ public class MiniMOTDConfigMigrator{
|
||||
|
||||
if(config.iconEnabled()){
|
||||
if(config.motds().size() == 1){
|
||||
MiniMOTDConfig.Motd motd = config.motds().get(0);
|
||||
if(!motd.icon().isEmpty() && !motd.icon().equalsIgnoreCase("random")){
|
||||
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() && !motd.icon().equalsIgnoreCase("random")){
|
||||
if(!motd.icon().isEmpty()){
|
||||
profilesBuilders.get(i).favicon(motd.icon());
|
||||
}
|
||||
}
|
||||
@@ -147,14 +149,14 @@ public class MiniMOTDConfigMigrator{
|
||||
|
||||
boolean profilesInvalid = profiles.isEmpty() || profiles.stream().allMatch(ProfileEntry::isInvalid);
|
||||
if(entry.isInvalid() && profilesInvalid){
|
||||
logger.warn("[Migrator - MiniMOTD] Main ProfileEntry was invalid and profiles were empty.");
|
||||
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.getPlugin().getFolderPath().resolve("profiles").resolve("minimotd_migrated.yml");
|
||||
Path profile = core.plugin().folderPath().resolve("profiles").resolve("minimotd_migrated.yml");
|
||||
if(Files.exists(profile)){
|
||||
logger.warn("[Migrator - MiniMOTD] Cannot create new file minimotd_migrated.yml. One with the same name is already present.");
|
||||
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;
|
||||
@@ -163,7 +165,7 @@ public class MiniMOTDConfigMigrator{
|
||||
try{
|
||||
Files.createFile(profile);
|
||||
}catch(IOException ex){
|
||||
logger.warn("[Migrator - MiniMOTD] Encountered an IOException while trying to create file minimotd_migrated.yml.", 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;
|
||||
@@ -180,14 +182,14 @@ public class MiniMOTDConfigMigrator{
|
||||
try{
|
||||
migrated = yamlLoader.load();
|
||||
}catch(IOException ex){
|
||||
logger.warn("[Migrator - MiniMOTD] Encountered an IOException while trying to load file minimotd_migrated.yml.", 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("[Migrator - MiniMOTD] Cannot migrate Configuration. ConfigurationNode was null.");
|
||||
logger.warn("[<white>Migrator - MiniMOTD</white>] Cannot migrate Configuration. ConfigurationNode was null.");
|
||||
sender.sendErrorMsg(" -> <red>File loading error.");
|
||||
|
||||
return false;
|
||||
@@ -202,7 +204,7 @@ public class MiniMOTDConfigMigrator{
|
||||
|
||||
migrated.set(entry);
|
||||
}catch(SerializationException ex){
|
||||
logger.warn("[Migrator - MiniMOTD] Encountered a SerializationException while setting values.", 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;
|
||||
@@ -214,7 +216,7 @@ public class MiniMOTDConfigMigrator{
|
||||
|
||||
return true;
|
||||
}catch(IOException ex){
|
||||
logger.warn("[Migrator - MiniMOTD] Encountered an IOException while trying to save new file minimotd_migrated.yml.", 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;
|
||||
|
||||
+50
-44
@@ -59,26 +59,55 @@ public class SLPConfigMigrator{
|
||||
"(?<end>#[a-fA-F0-9]{6})" + // Ending color
|
||||
"%" +
|
||||
"(?<text>.+?)" + // Text
|
||||
"%gradient%");
|
||||
"%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}");
|
||||
"%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.getPlugin().getPluginLogger();
|
||||
PluginLogger logger = core.plugin().pluginLogger();
|
||||
if(slpCore == null){
|
||||
logger.warn("[Migrator - ServerListPlus] Cannot migrate ServerListPlus config. ServerListPlus is not active!");
|
||||
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("[Migrator - ServerListPlus] Cannot migrate ServerListPlus configuration. Received configuration was null.");
|
||||
logger.warn("[<white>Migrator - ServerListPlus</white>] Cannot migrate ServerListPlus configuration. Received configuration was null.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -99,9 +128,9 @@ public class SLPConfigMigrator{
|
||||
}
|
||||
|
||||
private static int parseConf(AdvancedServerList<?> core, CmdSender sender, PersonalizedStatusConf.StatusConf conf, Type type){
|
||||
PluginLogger logger = core.getPlugin().getPluginLogger();
|
||||
PluginLogger logger = core.plugin().pluginLogger();
|
||||
if(conf == null){
|
||||
logger.info("[Migrator - ServerListPlus] No StatusConf found for type %s. Skipping...", type.getName());
|
||||
logger.info("[<white>Migrator - ServerListPlus</white>] No StatusConf found for type <white>%s</white>. Skipping...", type.getName());
|
||||
sender.sendPrefixedMsg(" -> Not found! Skipping...");
|
||||
|
||||
return 0;
|
||||
@@ -141,15 +170,15 @@ public class SLPConfigMigrator{
|
||||
|
||||
boolean profilesInvalid = profileEntries.isEmpty() || profileEntries.stream().anyMatch(ProfileEntry::isInvalid);
|
||||
if(entry.isInvalid() && profilesInvalid){
|
||||
logger.warn("[Migrator - ServerListPlus] Unable to parse ServerListPlus configuration of type %s. Generated ProfileEntry was invalid.", type.getName());
|
||||
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.getPlugin().getFolderPath().resolve("profiles").resolve(type.getFile());
|
||||
Path profile = core.plugin().folderPath().resolve("profiles").resolve(type.getFile());
|
||||
if(Files.exists(profile)){
|
||||
logger.warn("[Migrator - ServerListPlus] Cannot create new file %s. One with the same name is already present", type.getFile());
|
||||
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;
|
||||
@@ -158,7 +187,7 @@ public class SLPConfigMigrator{
|
||||
try{
|
||||
Files.createFile(profile);
|
||||
}catch(IOException ex){
|
||||
logger.warn("[Migrator - ServerListPlus] Encountered an IOException while trying to create file %s.", ex, type.getFile());
|
||||
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;
|
||||
@@ -175,14 +204,14 @@ public class SLPConfigMigrator{
|
||||
try{
|
||||
node = loader.load();
|
||||
}catch(IOException ex){
|
||||
logger.warn("[Migrator - ServerListPlus] Encountered an IOException while trying to load file %s.", ex, type.getFile());
|
||||
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("[Migrator - ServerListPlus] Cannot migrate Configuration of type %s. ConfigurationNode was null.", type.getName());
|
||||
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;
|
||||
@@ -194,9 +223,7 @@ public class SLPConfigMigrator{
|
||||
|
||||
if(type == Type.PERSONALIZED){
|
||||
node.node("condition")
|
||||
.set("${player name} != \"" +
|
||||
core.getFileHandler().getString("Anonymous", "unknownPlayer", "name") +
|
||||
"\"");
|
||||
.set("${player name} != \"" + core.fileHandler().getString("Anonymous", "unknownPlayer", "name") + "\"");
|
||||
}else
|
||||
if(type == Type.BANNED){
|
||||
node.node("condition")
|
||||
@@ -209,7 +236,7 @@ public class SLPConfigMigrator{
|
||||
|
||||
node.set(entry);
|
||||
}catch(SerializationException ex){
|
||||
logger.warn("[Migrator - ServerListPlus] Encountered a SerializationException while setting values.", 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;
|
||||
@@ -221,7 +248,7 @@ public class SLPConfigMigrator{
|
||||
|
||||
return 1;
|
||||
}catch(IOException ex){
|
||||
logger.warn("[Migrator - ServerListPlus] Encountered an IOException while trying to save new file %s.", ex, type.getFile());
|
||||
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;
|
||||
@@ -372,32 +399,11 @@ public class SLPConfigMigrator{
|
||||
if(colorCodeMatcher.find()){
|
||||
builder.setLength(0);
|
||||
do{
|
||||
String color = colorCodeMatcher.group("color").toLowerCase(Locale.ROOT);
|
||||
switch(color){
|
||||
case "a" -> colorCodeMatcher.appendReplacement(builder, "<green>");
|
||||
case "b" -> colorCodeMatcher.appendReplacement(builder, "<aqua>");
|
||||
case "c" -> colorCodeMatcher.appendReplacement(builder, "<red>");
|
||||
case "d" -> colorCodeMatcher.appendReplacement(builder, "<light_purple>");
|
||||
case "e" -> colorCodeMatcher.appendReplacement(builder, "<yellow>");
|
||||
case "f" -> colorCodeMatcher.appendReplacement(builder, "<white>");
|
||||
case "k" -> colorCodeMatcher.appendReplacement(builder, "<obfuscated>");
|
||||
case "l" -> colorCodeMatcher.appendReplacement(builder, "<bold>");
|
||||
case "m" -> colorCodeMatcher.appendReplacement(builder, "<strikethrough>");
|
||||
case "n" -> colorCodeMatcher.appendReplacement(builder, "<underlined>");
|
||||
case "o" -> colorCodeMatcher.appendReplacement(builder, "<italic>");
|
||||
case "r" -> colorCodeMatcher.appendReplacement(builder, "<reset>");
|
||||
case "0" -> colorCodeMatcher.appendReplacement(builder, "<black>");
|
||||
case "1" -> colorCodeMatcher.appendReplacement(builder, "<dark_blue>");
|
||||
case "2" -> colorCodeMatcher.appendReplacement(builder, "<dark_green>");
|
||||
case "3" -> colorCodeMatcher.appendReplacement(builder, "<dark_aqua>");
|
||||
case "4" -> colorCodeMatcher.appendReplacement(builder, "<dark_red>");
|
||||
case "5" -> colorCodeMatcher.appendReplacement(builder, "<dark_purple>");
|
||||
case "6" -> colorCodeMatcher.appendReplacement(builder, "<gold>");
|
||||
case "7" -> colorCodeMatcher.appendReplacement(builder, "<grey>");
|
||||
case "8" -> colorCodeMatcher.appendReplacement(builder, "<dark_grey>");
|
||||
case "9" -> colorCodeMatcher.appendReplacement(builder, "<blue>");
|
||||
// This one shouldn't be reachable given the pattern, but you never know...
|
||||
default -> colorCodeMatcher.appendReplacement(builder, "&" + color);
|
||||
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());
|
||||
|
||||
|
||||
+21
-3
@@ -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;
|
||||
}
|
||||
|
||||
@@ -73,4 +87,8 @@ public class ComponentParser{
|
||||
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){}
|
||||
}
|
||||
+81
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-9
@@ -46,18 +46,18 @@ public record ServerListProfile(int priority, String condition, ProfileEntry def
|
||||
return true;
|
||||
|
||||
ParseWarnCollector collector = new ParseWarnCollector(condition);
|
||||
PluginLogger logger = core.getPlugin().getPluginLogger();
|
||||
PluginLogger logger = core.plugin().pluginLogger();
|
||||
|
||||
boolean result = core.getParser().evaluate(condition, player, server, collector);
|
||||
boolean result = core.parser().evaluate(condition, player, server, collector);
|
||||
|
||||
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()){
|
||||
logger.warn(" - At position %s:", context.position() == -1 ? "Unknown" : context.position());
|
||||
logger.warn(" %s", condition);
|
||||
logger.warn(" ".repeat(context.position() + 4) + "^");
|
||||
logger.warn(" -> %s", context.message());
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ public record ServerListProfile(int priority, String condition, ProfileEntry def
|
||||
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;
|
||||
@@ -159,7 +159,7 @@ public record ServerListProfile(int priority, String condition, ProfileEntry def
|
||||
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;
|
||||
}
|
||||
|
||||
+8
-1
@@ -76,7 +76,14 @@ public class CustomExpressionTokenizer{
|
||||
}
|
||||
}
|
||||
|
||||
collector.appendWarningFormatted(position.getIndex(), "Illegal Token '%c'.", text.charAt(position.getIndex()));
|
||||
if(position.getIndex() >= text.length()){
|
||||
collector.appendWarningFormatted(
|
||||
"Current index position (%d) is larger or equal to Text length (%d) for condition '%s'",
|
||||
position.getIndex(), text.length(), text
|
||||
);
|
||||
}else{
|
||||
collector.appendWarningFormatted(position.getIndex(), "Illegal Token '%c'.", text.charAt(position.getIndex()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+257
-98
@@ -1,28 +1,3 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.profiles.handlers;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
@@ -42,10 +17,12 @@ import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class FaviconHandler<F>{
|
||||
@@ -55,156 +32,338 @@ public class FaviconHandler<F>{
|
||||
private final ThreadPoolExecutor faviconThreadPool;
|
||||
private final Cache<String, CompletableFuture<F>> faviconCache;
|
||||
|
||||
private final Map<String, F> localFavicons = new HashMap<>();
|
||||
private final Random random = new Random();
|
||||
private final HttpClient client = HttpClient.newHttpClient();
|
||||
private final Map<String, FaviconPair<F>> localFavicons = new HashMap<>();
|
||||
|
||||
public FaviconHandler(AdvancedServerList<F> core){
|
||||
this.core = core;
|
||||
this.logger = core.getPlugin().getPluginLogger();
|
||||
this.faviconThreadPool = createFaviconThreadPool();
|
||||
this.logger = core.plugin().pluginLogger();
|
||||
this.faviconThreadPool = createThreadPool();
|
||||
this.faviconCache = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(core.getFileHandler().getLong(1, 1, "faviconCacheTime"), TimeUnit.MINUTES)
|
||||
.build();
|
||||
.expireAfterWrite(core.fileHandler().getLong(1, 1, "faviconCacheTime"), TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
loadLocalFavicons();
|
||||
}
|
||||
|
||||
public F getFavicon(String input){
|
||||
if(localFavicons.size() > 0){
|
||||
logger.debug(FaviconHandler.class, "Current Local Favicons:");
|
||||
for(String key : localFavicons.keySet()){
|
||||
logger.debug(FaviconHandler.class, " - %s", key);
|
||||
}
|
||||
}
|
||||
|
||||
public F favicon(String input){
|
||||
if(localFavicons.containsKey(input.toLowerCase(Locale.ROOT))){
|
||||
logger.debug(FaviconHandler.class, "Input matches local favicon Image. Returning it...");
|
||||
return localFavicons.get(input.toLowerCase(Locale.ROOT));
|
||||
logger.debug(FaviconHandler.class, "Returning local Favicon '%s'.", input);
|
||||
|
||||
return localFavicons.get(input.toLowerCase(Locale.ROOT)).favicon();
|
||||
}
|
||||
|
||||
try{
|
||||
logger.debug(FaviconHandler.class, "Getting Favicon image from cache...");
|
||||
return faviconCache.get(input, () -> getFuture(input)).getNow(null);
|
||||
}catch(ExecutionException ex){
|
||||
logger.warn("Received ExecutionException while retrieving Favicon for '%s'.", ex, input);
|
||||
return null;
|
||||
if(input.equalsIgnoreCase("random")){
|
||||
logger.debug(FaviconHandler.class, "Returning random local Favicon.");
|
||||
|
||||
FaviconPair<F> pair = randomFavicon();
|
||||
if(pair == null){
|
||||
logger.warn("Cannot return random Favicon. Make sure PNGs are available in the Favicons folder!");
|
||||
return null;
|
||||
}
|
||||
|
||||
return pair.favicon();
|
||||
}
|
||||
|
||||
CompletableFuture<F> favicon = faviconCache.getIfPresent(input);
|
||||
if(favicon != null)
|
||||
return favicon.getNow(null);
|
||||
|
||||
logger.debug(FaviconHandler.class, "Processing '%s'", input);
|
||||
if(input.contains(";")){
|
||||
String[] parts = input.split(";");
|
||||
logger.debug(FaviconHandler.class, "Handling multi-part Favicon: ['%s']", String.join("', '", parts));
|
||||
favicon = CompletableFuture.supplyAsync(() -> {
|
||||
List<BufferedImage> images = new ArrayList<>();
|
||||
Arrays.stream(parts).forEach(part -> {
|
||||
logger.debug(FaviconHandler.class, "Creating BufferedImage for part '%s'...", part);
|
||||
images.add(create(part));
|
||||
});
|
||||
|
||||
if(images.isEmpty()){
|
||||
logger.debugWarn(FaviconHandler.class, "Unable to create Multi-part Favicon. List was empty.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return composeFavicon(images);
|
||||
}, faviconThreadPool);
|
||||
}else{
|
||||
logger.debug(FaviconHandler.class, "Handling Favicon input '%s'...", input);
|
||||
|
||||
favicon = CompletableFuture.supplyAsync(() -> {
|
||||
logger.debug(FaviconHandler.class, "Creating BufferedImage for '%s'...", input);
|
||||
|
||||
BufferedImage image = create(input);
|
||||
if(image == null){
|
||||
logger.warn("Cannot create Favicon for '%s'. BufferedImage was null!", input);
|
||||
return null;
|
||||
}
|
||||
|
||||
return createFavicon(image);
|
||||
}, faviconThreadPool);
|
||||
}
|
||||
|
||||
faviconCache.put(input, favicon);
|
||||
return favicon.getNow(null);
|
||||
}
|
||||
|
||||
public void cleanCache(){
|
||||
localFavicons.clear();
|
||||
faviconCache.invalidateAll();
|
||||
loadLocalFavicons();
|
||||
}
|
||||
|
||||
private CompletableFuture<F> getFuture(String input){
|
||||
private F composeFavicon(List<BufferedImage> images){
|
||||
logger.debug(FaviconHandler.class, "Composing Favicon out of %d part(s)...", images.size());
|
||||
BufferedImage image = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D graphics = image.createGraphics();
|
||||
|
||||
String strategy = core.fileHandler().getString("resize", "faviconStrategy");
|
||||
int layer = 0;
|
||||
for(BufferedImage img : images){
|
||||
layer++;
|
||||
logger.debug(FaviconHandler.class, "Applying layer %d...", layer);
|
||||
if(img == null){
|
||||
logger.debugWarn(FaviconHandler.class, "Layer %d was null! Skipping...", layer);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(strategy.toLowerCase(Locale.ROOT)){
|
||||
case "center" -> {
|
||||
int height = img.getHeight();
|
||||
int width = img.getWidth();
|
||||
|
||||
int posHeight = 0;
|
||||
int posWidth = 0;
|
||||
if(height != 64){
|
||||
posHeight = (64 - height) / 2;
|
||||
}
|
||||
|
||||
// 64 - 128 = -64
|
||||
|
||||
if(width != 64){
|
||||
posWidth = (64 - width) / 2;
|
||||
}
|
||||
|
||||
graphics.drawImage(img, posWidth, posHeight, img.getWidth(), img.getHeight(), null);
|
||||
}
|
||||
case "none" -> graphics.drawImage(img, 0, 0, img.getWidth(), img.getHeight(), null);
|
||||
default -> graphics.drawImage(img, 0, 0, 64, 64, null);
|
||||
}
|
||||
|
||||
logger.debug(FaviconHandler.class, "Applied layer %d!", layer);
|
||||
}
|
||||
|
||||
graphics.dispose();
|
||||
|
||||
logger.debug(FaviconHandler.class, "Image Composing complete! Creating Favicon instance...");
|
||||
return createFavicon(image);
|
||||
}
|
||||
|
||||
private BufferedImage create(String input){
|
||||
if(input.toLowerCase(Locale.ROOT).startsWith("https://")){
|
||||
logger.debug(FaviconHandler.class, "Resolving URL '%s'...", input);
|
||||
return CompletableFuture.supplyAsync(() -> fromURL(core, input), this.faviconThreadPool);
|
||||
logger.debug(FaviconHandler.class, "Handling URL '%s'...", input);
|
||||
return fromURL(input);
|
||||
}else
|
||||
if(input.equalsIgnoreCase("random")){
|
||||
logger.debug(FaviconHandler.class, "Returning random local Favicon...");
|
||||
FaviconPair<F> pair = randomFavicon();
|
||||
return pair == null ? null : pair.image();
|
||||
}else
|
||||
if(input.toLowerCase(Locale.ROOT).endsWith(".png")){
|
||||
logger.debug(FaviconHandler.class, "Resolving image file '%s'...", input);
|
||||
return CompletableFuture.completedFuture(localFavicons.get(input.toLowerCase(Locale.ROOT)));
|
||||
logger.debug(FaviconHandler.class, "Returning local Favicon '%s'...", input);
|
||||
FaviconPair<F> pair = localFavicons.get(input.toLowerCase(Locale.ROOT));
|
||||
return pair == null ? null : pair.image();
|
||||
}else{
|
||||
logger.debug(FaviconHandler.class, "Resolving Name/UUID as https://mc-heads.net/avatar/%s/64...", input);
|
||||
return CompletableFuture.supplyAsync(() -> fromURL(core, "https://mc-heads.net/avatar/" + input + "/64"), this.faviconThreadPool);
|
||||
logger.debug(FaviconHandler.class, "Handling Player Name/UUID '%s'...", input);
|
||||
return fromURL("https://mc-heads.net/avatar/" + input + "/64");
|
||||
}
|
||||
}
|
||||
|
||||
private FaviconPair<F> randomFavicon(){
|
||||
if(localFavicons.isEmpty())
|
||||
return null;
|
||||
|
||||
List<FaviconPair<F>> favicons = new ArrayList<>(localFavicons.values());
|
||||
if(favicons.isEmpty())
|
||||
return null;
|
||||
|
||||
if(favicons.size() == 1)
|
||||
return favicons.getFirst();
|
||||
|
||||
synchronized(random){
|
||||
return favicons.get(random.nextInt(favicons.size()));
|
||||
}
|
||||
}
|
||||
|
||||
private void loadLocalFavicons(){
|
||||
this.localFavicons.clear();
|
||||
|
||||
logger.info("Loading local image files as Favicons...");
|
||||
logger.info("[-] Loading local images as Favicons...");
|
||||
|
||||
Path folder = core.getPlugin().getFolderPath().resolve("favicons");
|
||||
Path folder = core.plugin().folderPath().resolve("favicons");
|
||||
if(!Files.exists(folder)){
|
||||
try{
|
||||
Files.createDirectories(folder);
|
||||
logger.info("Created favicons folder.");
|
||||
logger.debugSuccess(FaviconHandler.class, "Created favicons folder");
|
||||
}catch(IOException ex){
|
||||
logger.warn("Cannot create favicons folder. Encountered an IOException.", ex);
|
||||
logger.warn("Encountered IOException while creating favicons folder.", ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try(Stream<Path> pathStream = Files.list(folder)){
|
||||
pathStream.filter(Files::isRegularFile)
|
||||
.filter(file -> file.getFileName().toString().endsWith(".png"))
|
||||
.forEach(this::loadFile);
|
||||
try(Stream<Path> stream = Files.list(folder)){
|
||||
stream.filter(Files::isRegularFile)
|
||||
.filter(file -> file.getFileName().toString().endsWith(".png"))
|
||||
.forEach(this::loadFile);
|
||||
|
||||
logger.success("Loaded <white>%s</white> local Favicon(s).", localFavicons.size());
|
||||
}catch(IOException ex){
|
||||
logger.warn("Cannot load files from Favicons folder.", ex);
|
||||
if(core.fileHandler().getBool(false, "debug")){
|
||||
logger.warn("Encountered IOException while loading local favicons.", ex);
|
||||
}else{
|
||||
logger.warn("Encountered IOException while loading local favicons. Reason: %s", ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFile(Path path){
|
||||
try(InputStream stream = Files.newInputStream(path)){
|
||||
F favicon = createFavicon(stream);
|
||||
if(favicon == null){
|
||||
logger.warn("Cannot create Favicon from file '%s'. Received Favicon was null.", path.getFileName().toString());
|
||||
BufferedImage image = createBufferedImage(stream);
|
||||
if(image == null)
|
||||
return;
|
||||
}
|
||||
|
||||
localFavicons.put(path.getFileName().toString().toLowerCase(Locale.ROOT), favicon);
|
||||
logger.info("Loaded file '%s' as Favicon.", path.getFileName().toString());
|
||||
F favicon = core.plugin().createFavicon(image);
|
||||
if(favicon == null)
|
||||
return;
|
||||
|
||||
localFavicons.put(path.getFileName().toString().toLowerCase(Locale.ROOT), new FaviconPair<>(image, favicon));
|
||||
}catch(IOException ex){
|
||||
logger.warn("Cannot create Favicon from file '%s'. Encountered IOException.", ex);
|
||||
if(core.fileHandler().getBool(false, "debug")){
|
||||
logger.warn("Encountered IOException while creating a Favicon from '%s'.", ex, path.getFileName().toString());
|
||||
}else{
|
||||
logger.warn("Encountered IOException while creating a Favicon from '%s'. Reason: %s", path.getFileName().toString(), ex.getMessage());
|
||||
}
|
||||
}catch(Exception ex){
|
||||
if(core.fileHandler().getBool(false, "debug")){
|
||||
logger.warn("Encountered Exception while creating a Favicon from '%s'.", ex, path.getFileName().toString());
|
||||
}else{
|
||||
logger.warn("Encountered Exception while creating a Favicon from '%s'. Reason: %s", path.getFileName().toString(), ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private F fromURL(AdvancedServerList<F> core, String url){
|
||||
private BufferedImage fromURL(String url){
|
||||
try{
|
||||
logger.debug(FaviconHandler.class, "Creating Request for URL '%s'...", url);
|
||||
logger.debug(FaviconHandler.class, "Creating request for URL '%s'...", url);
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(new URI(url))
|
||||
.header("User-Agent", "AdvancedServerList-" + core.getPlugin().getLoader() + "/" + core.getVersion())
|
||||
.build();
|
||||
.uri(new URI(url))
|
||||
.header("User-Agent", "AdvancedServerList-" + core.plugin().loader() + "/" + core.version())
|
||||
.build();
|
||||
|
||||
try(InputStream stream = client.send(request, HttpResponse.BodyHandlers.ofInputStream()).body()){
|
||||
return createFavicon(stream);
|
||||
}catch(InterruptedException | IOException ex){
|
||||
logger.warn("Cannot create Favicon from URL '%s'. Encountered an Exception.", ex, url);
|
||||
return createBufferedImage(stream);
|
||||
}catch(IOException ex){
|
||||
if(core.fileHandler().getBool(false, "debug")){
|
||||
logger.warn("Encountered IOException while performing a web request for '%s'.", ex, url);
|
||||
}else{
|
||||
logger.warn("Encountered IOException while performing a web request for '%'. Reason: %s", url, ex.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}catch(InterruptedException ex){
|
||||
if(core.fileHandler().getBool(false, "debug")){
|
||||
logger.warn("Encountered an InterruptedException while performing a web request for '%s'.", ex, url);
|
||||
}else{
|
||||
logger.warn("Encountered an InterruptedException while performing a web request for '%s'. Reason: %s", url, ex.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}catch(URISyntaxException ex){
|
||||
logger.warn("Cannot create Favicon from URL '%s'. Encountered an IOException.", ex, url);
|
||||
if(core.fileHandler().getBool(false, "debug")){
|
||||
logger.warn("Encountered an URISyntaxException while performing a web request for '%s'.", ex, url);
|
||||
}else{
|
||||
logger.warn("Encountered an URISyntaxException while performing a web request for '%s'. Reason: %s", url, ex.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private F createFavicon(InputStream stream){
|
||||
private BufferedImage createBufferedImage(InputStream stream){
|
||||
logger.debug(FaviconHandler.class, "Creating BufferedImage from InputStream...");
|
||||
try{
|
||||
logger.debug(FaviconHandler.class, "Creating BufferedImage from InputStream...");
|
||||
BufferedImage original = ImageIO.read(stream);
|
||||
if(original == null){
|
||||
logger.warn("Cannot create Favicon. Received null BufferedImage.");
|
||||
logger.warn("Cannot create Favicon. Received a null BufferedImage!");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Don't waste resources resizing images already having right size.
|
||||
if(original.getWidth() == 64 && original.getHeight() == 64){
|
||||
logger.debug(FaviconHandler.class, "BufferedImage is 64x64 pixels. No resizing needed.");
|
||||
return core.getPlugin().createFavicon(original);
|
||||
logger.debug(FaviconHandler.class, "Check image size for possible resizing...");
|
||||
if(original.getHeight() == 64 && original.getWidth() == 64){
|
||||
logger.debug(FaviconHandler.class, "Image is 64x64. No resizing needed!");
|
||||
return original;
|
||||
}
|
||||
|
||||
logger.debug(FaviconHandler.class, "Resizing image to 64x64 pixels...");
|
||||
logger.debug(FaviconHandler.class, "Applying Favicon resize strategy...");
|
||||
BufferedImage image = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D graphics2D = image.createGraphics();
|
||||
Graphics2D graphics = image.createGraphics();
|
||||
|
||||
graphics2D.drawImage(original, 0, 0, 64, 64, null);
|
||||
graphics2D.dispose();
|
||||
String resizeMode = core.fileHandler().getString("resize", "faviconStrategy");
|
||||
switch(resizeMode.toLowerCase(Locale.ROOT)){
|
||||
case "center" -> {
|
||||
int height = original.getHeight();
|
||||
int width = original.getWidth();
|
||||
|
||||
int posHeight = 0;
|
||||
int posWidth = 0;
|
||||
if(height != 64){
|
||||
posHeight = (64 - height) / 2;
|
||||
}
|
||||
|
||||
if(width != 64){
|
||||
posWidth = (64 - width) / 2;
|
||||
}
|
||||
|
||||
graphics.drawImage(original, posWidth, posHeight, original.getWidth(), original.getHeight(), null);
|
||||
}
|
||||
case "none" -> graphics.drawImage(original, 0, 0, original.getWidth(), original.getHeight(), null);
|
||||
default -> graphics.drawImage(original, 0, 0, 64, 64, null);
|
||||
}
|
||||
|
||||
logger.debug(FaviconHandler.class, "Image resized! Returning Favicon...");
|
||||
return core.getPlugin().createFavicon(image);
|
||||
}catch(Exception ex){
|
||||
logger.warn("Unable to create Favicon. Received Exception.", ex);
|
||||
graphics.dispose();
|
||||
|
||||
logger.debug(FaviconHandler.class, "Image resized. Returning it...");
|
||||
return image;
|
||||
}catch(IOException ex){
|
||||
logger.warn("Encountered an IOException while processing Favicon!", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ThreadPoolExecutor createFaviconThreadPool(){
|
||||
private F createFavicon(BufferedImage image){
|
||||
try{
|
||||
return core.plugin().createFavicon(image);
|
||||
}catch(Exception ex){
|
||||
if(core.fileHandler().getBool(false, "debug")){
|
||||
logger.warn("Encountered an Exception while trying to create a Favicon instance.", ex);
|
||||
}else{
|
||||
logger.warn("Encountered an Exception while trying to create a Favicon instance. Reason: %s", ex.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ThreadPoolExecutor createThreadPool(){
|
||||
return new ThreadPoolExecutor(3, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024), r -> {
|
||||
Thread t = new Thread(r, "AdvancedServerList-FaviconThread");
|
||||
t.setDaemon(true);
|
||||
|
||||
return t;
|
||||
});
|
||||
}
|
||||
|
||||
private record FaviconPair<F>(BufferedImage image, F favicon){}
|
||||
}
|
||||
+16
-16
@@ -61,8 +61,8 @@ public class PlayerHandler{
|
||||
|
||||
public PlayerHandler(AdvancedServerList<?> core){
|
||||
this.core = core;
|
||||
this.logger = core.getPlugin().getPluginLogger();
|
||||
this.cache = core.getPlugin().getFolderPath().resolve("playercache.json");
|
||||
this.logger = core.plugin().pluginLogger();
|
||||
this.cache = core.plugin().folderPath().resolve("playercache.json");
|
||||
}
|
||||
|
||||
public void load(){
|
||||
@@ -71,7 +71,7 @@ public class PlayerHandler{
|
||||
return;
|
||||
}
|
||||
|
||||
if(core.getFileHandler().getBoolean("disableCache")){
|
||||
if(core.fileHandler().getBoolean("disableCache")){
|
||||
logger.info("'disableCache' is set to true. Skipping playercache.json loading...");
|
||||
return;
|
||||
}
|
||||
@@ -79,52 +79,52 @@ public class PlayerHandler{
|
||||
try(BufferedReader reader = Files.newBufferedReader(cache)){
|
||||
cachedPlayers = gson.fromJson(reader, listType);
|
||||
}catch(IOException ex){
|
||||
logger.warn("Encountered IOException while reading the playercache.json file!", ex);
|
||||
logger.warn("Encountered IOException while reading the <white>playercache.json</white> file!", ex);
|
||||
return;
|
||||
}catch(JsonIOException | JsonSyntaxException ex){
|
||||
logger.warn("Encountered JsonSyntaxException while parsing playercache.json file!", ex);
|
||||
logger.warn("Encountered JsonSyntaxException while parsing <white>playercache.json</white> file!", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
// In case Gson messes up... I guess.
|
||||
if(cachedPlayers == null){
|
||||
logger.warn("Couldn't load players from playercache.json file. Is the JSON valid?");
|
||||
logger.warn("Couldn't load players from <white>playercache.json</white> file. Is the JSON valid?");
|
||||
// Create new ArrayList instance to avoid further issues.
|
||||
cachedPlayers = new ArrayList<>();
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("Loaded %d players into cache!", cachedPlayers.size());
|
||||
logger.success("Loaded <white>%d</white> players into cache!", cachedPlayers.size());
|
||||
}
|
||||
|
||||
public void save(){
|
||||
if(cachedPlayers.isEmpty()){
|
||||
logger.info("No players in cache to save. Skipping...");
|
||||
logger.debug(PlayerHandler.class, "No players in cache to save. Skipping...");
|
||||
return;
|
||||
}
|
||||
|
||||
if(core.getFileHandler().getBoolean("disableCache")){
|
||||
logger.info("'disable_cache' is set to true. Skipping saving of cached players...");
|
||||
if(core.fileHandler().getBoolean("disableCache")){
|
||||
logger.debug(PlayerHandler.class, "[-] 'disable_cache' is set to true. Not saving cached players.");
|
||||
return;
|
||||
}
|
||||
|
||||
try(BufferedWriter writer = Files.newBufferedWriter(cache, StandardCharsets.UTF_8)){
|
||||
gson.toJson(cachedPlayers, listType, writer);
|
||||
logger.info("Successfully saved playercache.json file.");
|
||||
logger.success("Successfully saved <white>playercache.json</white> file.");
|
||||
}catch(IOException ex){
|
||||
logger.warn("Encountered IOException while saving cached players to playercache.json!", ex);
|
||||
logger.failure("Encountered IOException while saving cached players to <white>playercache.json</white>!", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void addPlayer(String ip, String name, UUID uuid){
|
||||
if(contains(ip) || core.getFileHandler().getBoolean("disableCache"))
|
||||
if(contains(ip) || core.fileHandler().getBoolean("disableCache"))
|
||||
return;
|
||||
|
||||
cachedPlayers.add(new CachedPlayer(ip, name, uuid));
|
||||
}
|
||||
|
||||
public CachedPlayer getCachedPlayer(String key){
|
||||
if(!contains(key) || core.getFileHandler().getBoolean("disableCache"))
|
||||
if(!contains(key) || core.fileHandler().getBoolean("disableCache"))
|
||||
return getDefaultPlayer();
|
||||
|
||||
for(CachedPlayer player : cachedPlayers){
|
||||
@@ -164,8 +164,8 @@ public class PlayerHandler{
|
||||
|
||||
return (defaultPlayer = new CachedPlayer(
|
||||
"0.0.0.0",
|
||||
core.getFileHandler().getString("Anonymous", "unknownPlayer", "name"),
|
||||
convertToUUID(core.getFileHandler().getString(defaultUUID.toString(), "unknownPlayer", "uuid"))
|
||||
core.fileHandler().getString("Anonymous", "unknownPlayer", "name"),
|
||||
convertToUUID(core.fileHandler().getString(defaultUUID.toString(), "unknownPlayer", "uuid"))
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
+11
-14
@@ -56,6 +56,7 @@ public class ProfileManager{
|
||||
NullBool extraPlayersEnabled = resolve(defEntry, entry, ProfileEntry::extraPlayersEnabled);
|
||||
NullBool maxPlayersEnabled = resolve(defEntry, entry, ProfileEntry::maxPlayersEnabled);
|
||||
NullBool onlinePlayersEnabled = resolve(defEntry, entry, ProfileEntry::onlinePlayersEnabled);
|
||||
NullBool hidePlayersHoverEnabled = resolve(defEntry, entry, ProfileEntry::hidePlayersHoverEnabled);
|
||||
String extraPlayersCount = resolve(defEntry, entry, ProfileEntry::extraPlayersCount);
|
||||
String maxPlayersCount = resolve(defEntry, entry, ProfileEntry::maxPlayersCount);
|
||||
String onlinePlayersCount = resolve(defEntry, entry, ProfileEntry::onlinePlayersCount);
|
||||
@@ -69,6 +70,7 @@ public class ProfileManager{
|
||||
.extraPlayersEnabled(extraPlayersEnabled)
|
||||
.maxPlayersEnabled(maxPlayersEnabled)
|
||||
.onlinePlayersEnabled(onlinePlayersEnabled)
|
||||
.hidePlayersHoverEnabled(hidePlayersHoverEnabled)
|
||||
.extraPlayersCount(extraPlayersCount)
|
||||
.maxPlayersCount(maxPlayersCount)
|
||||
.onlinePlayersCount(onlinePlayersCount)
|
||||
@@ -76,7 +78,7 @@ public class ProfileManager{
|
||||
}
|
||||
|
||||
public static ServerListProfile resolveProfile(AdvancedServerList<?> core, GenericPlayer player, GenericServer server){
|
||||
for(ServerListProfile profile : core.getFileHandler().getProfiles()){
|
||||
for(ServerListProfile profile : core.fileHandler().profiles()){
|
||||
if(profile.isInvalidProfile())
|
||||
continue;
|
||||
|
||||
@@ -96,6 +98,7 @@ public class ProfileManager{
|
||||
NullBool extraPlayersEnabled = resolveNullBool(node, "playerCount", "extraPlayers", "enabled");
|
||||
NullBool maxPlayersEnabled = resolveNullBool(node, "playerCount", "maxPlayers", "enabled");
|
||||
NullBool onlinePlayersEnabled = resolveNullBool(node, "playerCount", "onlinePlayers", "enabled");
|
||||
NullBool hidePlayersHoverEnabled = resolveNullBool(node, "playerCount", "hidePlayersHover");
|
||||
String extraPlayers = resolveNullableString(node, "playerCount", "extraPlayers", "amount");
|
||||
String maxPlayers = resolveNullableString(node, "playerCount", "maxPlayers", "amount");
|
||||
String onlinePlayers = resolveNullableString(node, "playerCount", "onlinePlayers", "amount");
|
||||
@@ -109,6 +112,7 @@ public class ProfileManager{
|
||||
.extraPlayersEnabled(extraPlayersEnabled)
|
||||
.maxPlayersEnabled(maxPlayersEnabled)
|
||||
.onlinePlayersEnabled(onlinePlayersEnabled)
|
||||
.hidePlayersHoverEnabled(hidePlayersHoverEnabled)
|
||||
.extraPlayersCount(extraPlayers)
|
||||
.maxPlayersCount(maxPlayers)
|
||||
.onlinePlayersCount(onlinePlayers)
|
||||
@@ -116,20 +120,13 @@ public class ProfileManager{
|
||||
}
|
||||
|
||||
public static boolean checkOption(Object obj){
|
||||
if(obj == null)
|
||||
return false;
|
||||
return switch(obj){
|
||||
case List<?> list -> !list.isEmpty(); // Check if list isn't empty
|
||||
case String str -> !str.isEmpty(); // Check if String is not empty
|
||||
case NullBool nb -> nb.getOrDefault(false); // Return NullBool's value
|
||||
case null, default -> false;
|
||||
};
|
||||
|
||||
if(obj instanceof List<?> list){
|
||||
return !list.isEmpty(); // Check if list isn't empty
|
||||
}else
|
||||
if(obj instanceof String str){
|
||||
return !str.isEmpty(); // Check if String is not empty
|
||||
}else
|
||||
if(obj instanceof NullBool nb){
|
||||
return nb.getOrDefault(false); // Return NullBool's value
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static <T> T resolve(ProfileEntry defProfile, ProfileEntry profile, Function<ProfileEntry, T> function){
|
||||
|
||||
+1
@@ -53,6 +53,7 @@ public class ProfileSerializer implements TypeSerializer<ProfileEntry>{
|
||||
node.node("playerCount", "text").set(profile.playerCountText());
|
||||
node.node("favicon").set(profile.favicon());
|
||||
node.node("playerCount", "hidePlayers").set(profile.hidePlayersEnabled().getOrDefault(false));
|
||||
node.node("playerCount", "hidePlayersHover").set(profile.hidePlayersHoverEnabled().getOrDefault(false));
|
||||
node.node("playerCount", "extraPlayers", "enabled").set(profile.extraPlayersEnabled().getOrDefault(false));
|
||||
node.node("playerCount", "extraPlayers", "amount").set((profile.extraPlayersCount() == null) ? "0" : profile.extraPlayersCount());
|
||||
node.node("playerCount", "maxPlayers", "enabled").set(profile.maxPlayersEnabled().getOrDefault(false));
|
||||
|
||||
+31
-8
@@ -34,9 +34,14 @@ import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.toke
|
||||
import ch.andre601.expressionparser.ParseWarnCollector;
|
||||
|
||||
import java.text.ParsePosition;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class StringReplacer{
|
||||
|
||||
public static StringModifier modifier(String text){
|
||||
return new StringModifier(text);
|
||||
}
|
||||
|
||||
public static String replace(String input, GenericPlayer player, GenericServer server){
|
||||
if(input == null)
|
||||
return null;
|
||||
@@ -63,7 +68,7 @@ public class StringReplacer{
|
||||
}
|
||||
|
||||
private static String parsePlaceholder0(String input, ParsePosition position, GenericPlayer player, GenericServer server, ParseWarnCollector collector){
|
||||
AdvancedServerListAPI api = AdvancedServerList.getApi();
|
||||
AdvancedServerListAPI api = AdvancedServerList.api();
|
||||
int index = position.getIndex();
|
||||
|
||||
StringBuilder identifier = new StringBuilder();
|
||||
@@ -72,6 +77,9 @@ public class StringReplacer{
|
||||
boolean identified = false;
|
||||
boolean invalid = true;
|
||||
|
||||
int placeholderStart = position.getIndex() - 1;
|
||||
int valueStart = position.getIndex() - 1;
|
||||
|
||||
char[] chars = input.substring(index).toCharArray();
|
||||
for(final char c : chars){
|
||||
index++;
|
||||
@@ -83,6 +91,7 @@ public class StringReplacer{
|
||||
|
||||
if(c == ' ' && !identified){
|
||||
identified = true;
|
||||
valueStart = index + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -98,10 +107,6 @@ public class StringReplacer{
|
||||
|
||||
StringBuilder raw = new StringBuilder();
|
||||
|
||||
// Condition parsing needs one extra increase of index... for some reason
|
||||
if(collector != null)
|
||||
index++;
|
||||
|
||||
position.setIndex(index);
|
||||
|
||||
if(invalid){
|
||||
@@ -111,7 +116,7 @@ public class StringReplacer{
|
||||
raw.append(' ').append(valuesStr);
|
||||
|
||||
if(collector != null)
|
||||
collector.appendWarningFormatted(index, "Placeholder '%s' does not have a closing bracket (})", raw.toString());
|
||||
collector.appendWarningFormatted(index, "Placeholder '<white>%s</white>' does not have a closing bracket (})", raw.toString());
|
||||
|
||||
return raw.toString();
|
||||
}
|
||||
@@ -126,7 +131,7 @@ public class StringReplacer{
|
||||
raw.append('}');
|
||||
|
||||
if(collector != null)
|
||||
collector.appendWarningFormatted(index, "Placeholder '%s' does not have any available PlaceholderProvider", raw.toString());
|
||||
collector.appendWarningFormatted(placeholderStart, "Placeholder '<white>%s</white>' does not have any available PlaceholderProvider", raw.toString());
|
||||
|
||||
return raw.toString();
|
||||
}
|
||||
@@ -141,11 +146,29 @@ public class StringReplacer{
|
||||
raw.append('}');
|
||||
|
||||
if(collector != null)
|
||||
collector.appendWarningFormatted(index, "Placeholder '%s' has an invalid value String '%s'", raw.toString(), valuesStr);
|
||||
collector.appendWarningFormatted(valueStart, "Placeholder '<white>%s</white>' has an invalid value String '<white>%s</white>'", raw.toString(), valuesStr);
|
||||
|
||||
return raw.toString();
|
||||
}
|
||||
|
||||
return replacement;
|
||||
}
|
||||
|
||||
public static class StringModifier{
|
||||
private String value;
|
||||
|
||||
public StringModifier(String value){
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public StringModifier modify(Function<String, String> function){
|
||||
this.value = function.apply(value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public String asString(){
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -48,6 +48,14 @@ checkUpdates: true
|
||||
#
|
||||
debug: false
|
||||
|
||||
#
|
||||
# Should the "ASL" ASCI art be displayed on plugin startup?
|
||||
# Setting this to false will hide the banner.
|
||||
#
|
||||
# Default: true
|
||||
#
|
||||
printBanner: true
|
||||
|
||||
#
|
||||
# Sets how long Favicons should be cached. Value is in minutes with the lowest possible value being 1.
|
||||
#
|
||||
@@ -58,6 +66,19 @@ debug: false
|
||||
#
|
||||
faviconCacheTime: 1
|
||||
|
||||
#
|
||||
# Sets how AdvancedServerList should handle the size and positioning of images for favicons.
|
||||
#
|
||||
# Available options:
|
||||
#
|
||||
# - resize: Resizes the image to 64x64 pixels if it isn't these dimensions already.
|
||||
# - center: Doesn't resize the image, but instead tries to center it as good as possible in a 64x64 area.
|
||||
# - none: Doesn't resize the image and leaves it positioned at x 0 and y 0 (top-left) of the 64x64 area.
|
||||
#
|
||||
# Setting any not listed option or leaving this blank/removing it sets the option to resize.
|
||||
#
|
||||
faviconResizeStrategy: 'resize'
|
||||
|
||||
#
|
||||
# Options to disable while the Maintenance Plugin is present and Maintenance Mode is active.
|
||||
#
|
||||
@@ -84,6 +105,12 @@ disableDuringMaintenance:
|
||||
#
|
||||
hidePlayers: true
|
||||
#
|
||||
# Whether hiding player list on hover should be disabled while Maintenance mode is active.
|
||||
#
|
||||
# Default: true
|
||||
#
|
||||
hidePlayersHover: true
|
||||
#
|
||||
# Whether player count text changing should be disabled while Maintenance mode is active.
|
||||
#
|
||||
# Default: true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Set the priority of this profile.
|
||||
# If there are multiple profiles (files) will they be sorted by priority, starting with highest.
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/#priority
|
||||
# Read more: https://asl.andre601.ch/profiles/options/priority/
|
||||
#
|
||||
priority: 0
|
||||
|
||||
@@ -10,11 +10,11 @@ priority: 0
|
||||
# Set condition(s) that should return true to display this profile.
|
||||
# This allows you to create profiles to only show for specific scenarios (i.e. player is banned).
|
||||
#
|
||||
# Multiple conditions can be chaing with the keywords 'and', 'or', or their counterparts '&&' and '||'.
|
||||
# 'AND'/'&&' and 'OR'/'||' can be used to chain together multiple conditions.
|
||||
#
|
||||
# No condition or an empty condition will be treated as true by default.
|
||||
# Removing the condition option or setting it to an empty String will be treated as a true condition being returned.
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/#condition
|
||||
# Read more: https://asl.andre601.ch/profiles/options/condition
|
||||
#
|
||||
condition: ''
|
||||
|
||||
@@ -30,7 +30,7 @@ condition: ''
|
||||
#
|
||||
# When not present or empty (profiles: []), no profiles will be used and global options from the file will be used instead.
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/#profiles
|
||||
# Read more: https://asl.andre601.ch/profiles/options/profiles
|
||||
#
|
||||
profiles:
|
||||
- motd: ['<aqua>Line A', '<gold>Line B']
|
||||
@@ -40,14 +40,17 @@ profiles:
|
||||
|
||||
#
|
||||
# Set the "Message of the day" to display.
|
||||
# This option supports RGB colors for 1.16+ Servers and clients, including gradients using MiniMessage's <gradient>
|
||||
# option.
|
||||
# This option supports RGB colors for 1.16+ Servers and clients. More advanced MiniMessage features such as <gradient>
|
||||
# can be used for further styling the text.
|
||||
#
|
||||
# <center> can be put at the start of a line to have AdvancedServerList automatically center the text as good as
|
||||
# possible.
|
||||
#
|
||||
# Only the first two lines will be considered and any additional one is ignored.
|
||||
#
|
||||
# When not present or empty (motd: []), no MOTD will be set.
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/#motd
|
||||
# Read more: https://asl.andre601.ch/profiles/options/motd
|
||||
#
|
||||
motd:
|
||||
- '<grey>First Line'
|
||||
@@ -55,28 +58,36 @@ motd:
|
||||
|
||||
#
|
||||
# Set the Favicon (image on the left of a server entry) to display.
|
||||
# This option supports three types of inputs:
|
||||
# This option supports three types of input:
|
||||
#
|
||||
# 1. Name, including .png extension, pointing to an image located in the plugin's favicons folder.
|
||||
# 2. URL pointing to an image.*
|
||||
# 3. Player name, UUID or placeholders returning one of these.*
|
||||
# https://mc-heads.net will be used to retrieve an image of the provided name/UUID.
|
||||
# 1. Filename, including the .png extension, matching a filename in the favicons folder of the plugin.
|
||||
# - 'random' can be used to have AdvancedServerList select a random image from the favicons folder every time
|
||||
# the server is pinged.
|
||||
# 3. URL pointing to an image.
|
||||
# 4. Player name, UUID or a placeholder that returns either.
|
||||
# - https://mc-heads.net will be used to retrieve an image of the provided name/UUID.
|
||||
#
|
||||
# * These images are loaded asynchronously, meaning they may not yet be "ready" when AdvancedServerList is done with
|
||||
# the event, requiring a refresh of the server list by the player to have it shown.
|
||||
#
|
||||
# Any images above or below 64x64 pixels will be automatically resized to these dimensions.
|
||||
# Any image below or above 64x64 pixels will be resized according to the faviconResizeMode option in the config.yml.
|
||||
#
|
||||
# If not present or empty (favicon: ''), defaults to not modifying the favicon.
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/#favicon
|
||||
# Read more: https://asl.andre601.ch/profiles/options/favicon
|
||||
#
|
||||
# "The favicon isn't showing!"
|
||||
# This is a side effect of using either URLs or player names/UUIDs for the favicon.
|
||||
# These types require AdvancedServerList to create a new favicon object from scratch. This action takes a significant
|
||||
# amount of time, which can cause the server to show with a huge ping.
|
||||
# To avoid this is this action performed asynchronously. This means that the plugin doesn't wait for the favicon to
|
||||
# be created before continuing, which can result in no favicon being available when it is done handling the ping.
|
||||
# This should only happen for the first ping of a player and subsequent pings should display the favicon, if the
|
||||
# pings are done within the configured cache time (See 'faviconCacheTime' in the config.yml).
|
||||
#
|
||||
favicon: ''
|
||||
|
||||
#
|
||||
# Contains options for modifying the player count (Text displaying <online>/<max>).
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/#playercount
|
||||
# Read more: https://asl.andre601.ch/profiles/options/playercount
|
||||
#
|
||||
playerCount:
|
||||
#
|
||||
@@ -86,17 +97,26 @@ playerCount:
|
||||
#
|
||||
# If not present, defaults to false.
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/#hideplayers
|
||||
# Read more: https://asl.andre601.ch/profiles/options/playercount/hideplayers
|
||||
#
|
||||
hidePlayers: false
|
||||
#
|
||||
# Set whether the List of players is shown when hovering over the player count.
|
||||
# Setting this to true will also make the plugin ignore the hover option below.
|
||||
#
|
||||
# If not present, defaults to false.
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/options/playercount/hideplayershover
|
||||
#
|
||||
hidePlayersHover: false
|
||||
#
|
||||
# Set the lines shown when the player hovers over the player count.
|
||||
# Unlike the MOTD option does this one only support basic color and formatting codes. Any RGB colors will be
|
||||
# downsampled to the nearest matching color.
|
||||
# down-sampled to the nearest matching color.
|
||||
#
|
||||
# If not present or empty (hover: []), defaults to not modifying the hover.
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/#hover
|
||||
# Read more: https://asl.andre601.ch/profiles/options/playercount/hover
|
||||
#
|
||||
hover:
|
||||
- '<aqua>Line 1'
|
||||
@@ -110,7 +130,7 @@ playerCount:
|
||||
#
|
||||
# If not present or empty (text: ''), defaults to not modifying the text.
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/#text
|
||||
# Read more: https://asl.andre601.ch/profiles/options/playercount/text
|
||||
#
|
||||
text: ''
|
||||
#
|
||||
@@ -119,7 +139,7 @@ playerCount:
|
||||
#
|
||||
# Using this feature will modify the ${server playersMax} placeholder, except for usage in the condition option.
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/#extraplayers
|
||||
# Read more: https://asl.andre601.ch/profiles/options/playercount/extraplayers
|
||||
#
|
||||
extraPlayers:
|
||||
#
|
||||
@@ -129,7 +149,7 @@ playerCount:
|
||||
#
|
||||
# If not present, defaults to false.
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/#extraplayers-enabled
|
||||
# Read more: https://asl.andre601.ch/profiles/options/playercount/extraplayers#enabled
|
||||
#
|
||||
enabled: false
|
||||
#
|
||||
@@ -141,7 +161,7 @@ playerCount:
|
||||
#
|
||||
# If not present, defaults to 0.
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/#extraplayers-amount
|
||||
# Read more: https://asl.andre601.ch/profiles/options/playercount/extraplayers#amount
|
||||
#
|
||||
amount: 0
|
||||
#
|
||||
@@ -149,7 +169,7 @@ playerCount:
|
||||
#
|
||||
# Using this feature will modify the ${server playersMax} placeholder, except for usage in the condition option.
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/#maxplayers
|
||||
# Read more: https://asl.andre601.ch/profiles/options/playercount/maxplayers
|
||||
#
|
||||
maxPlayers:
|
||||
#
|
||||
@@ -158,7 +178,7 @@ playerCount:
|
||||
#
|
||||
# If not present, defaults to false.
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/#maxplayers-enabled
|
||||
# Read more: https://asl.andre601.ch/profiles/options/playercount/maxplayers#enabled
|
||||
#
|
||||
enabled: false
|
||||
#
|
||||
@@ -167,13 +187,13 @@ playerCount:
|
||||
#
|
||||
# If not present, defaults to 0.
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/#maxplayers-amount
|
||||
# Read more: https://asl.andre601.ch/profiles/options/playercount/maxplayers#amount
|
||||
#
|
||||
amount: 0
|
||||
#
|
||||
# The onlinePlayers feature allows to set a number to use as the online player count to display.
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/#onlineplayers
|
||||
# Read more: https://asl.andre601.ch/profiles/options/playercount/onlineplayers
|
||||
#
|
||||
onlinePlayers:
|
||||
#
|
||||
@@ -182,7 +202,7 @@ playerCount:
|
||||
#
|
||||
# If not present, defaults to false.
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/#onlineplayers-enabled
|
||||
# Read more: https://asl.andre601.ch/profiles/options/playercount/onlineplayers#enabled
|
||||
#
|
||||
enabled: false
|
||||
#
|
||||
@@ -191,6 +211,6 @@ playerCount:
|
||||
#
|
||||
# If not present, defaults to current online player count.
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/#onlineplayers-amount
|
||||
# Read more: https://asl.andre601.ch/profiles/options/playercount/onlineplayers#amount
|
||||
#
|
||||
amount: 0
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.test;
|
||||
package ch.andre601.advancedserverlist.core;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.test;
|
||||
package ch.andre601.advancedserverlist.core;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.ProfileConditionParser;
|
||||
import ch.andre601.expressionparser.ParseWarnCollector;
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.test;
|
||||
package ch.andre601.advancedserverlist.core;
|
||||
|
||||
import com.unascribed.flexver.FlexVerComparator;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -0,0 +1,15 @@
|
||||
def on_pre_page_macros(env):
|
||||
footer = """
|
||||
|
||||
{% if page.meta and page.meta.api %}
|
||||
{% include 'api_docs/constructor_summaries.md' %}
|
||||
{% include 'api_docs/nested_class_summaries.md' %}
|
||||
{% include 'api_docs/enum_summaries.md' %}
|
||||
{% include 'api_docs/method_summaries.md' %}
|
||||
|
||||
{% include 'api_docs/constructor_details.md' %}
|
||||
{% include 'api_docs/enum_details.md' %}
|
||||
{% include 'api_docs/method_details.md' %}
|
||||
{% endif %}
|
||||
"""
|
||||
env.markdown += footer
|
||||
@@ -0,0 +1,23 @@
|
||||
import re
|
||||
|
||||
from mkdocs.config.defaults import MkDocsConfig
|
||||
from mkdocs.structure.files import Files
|
||||
from mkdocs.structure.pages import Page
|
||||
from re import Match
|
||||
|
||||
def on_page_markdown(
|
||||
markdown: str, *, page: Page, config: MkDocsConfig, files: Files
|
||||
):
|
||||
def replace(match: Match):
|
||||
platform = match.group(1)
|
||||
if platform == "paper": return "[:fontawesome-solid-paper-plane:](https://papermc.io){ .md-button .md-button--icon title=\"PaperMC\" target=\"_blank\" rel=\"noopener\" }"
|
||||
elif platform == "bungee": return "[:simple-spigotmc:](https://spigotmc.org){ .md-button .md-button--icon title=\"BungeeCord\" target=\"_blank\" rel=\"noopener\" }"
|
||||
elif platform == "velocity": return "[:simple-velocity:](https://velocitypowered.com){ .md-button .md-button--icon title=\"Velocity\" target=\"_blank\" rel=\"noopener\" }"
|
||||
elif platform == "proxies": return "[:simple-spigotmc:](https://spigotmc.org){ .md-button .md-button--icon title=\"BungeeCord\" target=\"_blank\" rel=\"noopener\" } [:simple-velocity:](https://velocitypowered.com){ .md-button .md-button--icon title=\"Velocity\" target=\"_blank\" rel=\"noopener\" }"
|
||||
elif platform == "all": return "[:fontawesome-solid-paper-plane:](https://papermc.io){ .md-button .md-button--icon title=\"PaperMC\" target=\"_blank\" rel=\"noopener\" } [:simple-spigotmc:](https://spigotmc.org){ .md-button .md-button--icon title=\"BungeeCord\" target=\"_blank\" rel=\"noopener\" } [:simple-velocity:](https://velocitypowered.com){ .md-button .md-button--icon title=\"Velocity\" target=\"_blank\" rel=\"noopener\" }"
|
||||
else: return platform
|
||||
|
||||
return re.sub(
|
||||
r"<!-- icon:(paper|bungee|velocity|proxies|all) -->",
|
||||
replace, markdown, flags = re.I | re.M
|
||||
)
|
||||
@@ -17,7 +17,7 @@
|
||||
import re
|
||||
|
||||
from mkdocs.config.defaults import MkDocsConfig
|
||||
from mkdocs.structure.files import File, Files
|
||||
from mkdocs.structure.files import Files
|
||||
from mkdocs.structure.pages import Page
|
||||
|
||||
def on_page_markdown(
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% import "api-objects/info-template.html" as info %}
|
||||
|
||||
{% block content %}
|
||||
{% include "partials/content.html" %}
|
||||
{% if page.meta %}
|
||||
<!--
|
||||
Object summaries
|
||||
-->
|
||||
{% if page.meta.constructors %}
|
||||
{{ info.summary('Constructor Summary', page.meta.constructors, details_type='constructor', defaults=page.meta.defaults) }}
|
||||
{% endif %}
|
||||
{% if page.meta.classes %}
|
||||
{{ info.summary('Class Summary', page.meta.classes, details_type='class', defaults=page.meta.defaults) }}
|
||||
{% endif %}
|
||||
{% if page.meta.enums %}
|
||||
{{ info.summary('Enum Constant Summary', page.meta.enums, details_type='enum', defaults=page.meta.defaults) }}
|
||||
{% endif %}
|
||||
{% if page.meta.methods or page.meta.inherits %}
|
||||
{{ info.summary('Method Summary', page.meta.methods, page.meta.inherits, details_type='method', defaults=page.meta.defaults) }}
|
||||
{% endif %}
|
||||
<!--
|
||||
Detailed object info
|
||||
-->
|
||||
{% if page.meta.constructors %}
|
||||
{{ info.info('Constructor Detail', page.meta.constructors, 'constructor', defaults=page.meta.defaults) }}
|
||||
{% endif %}
|
||||
{% if page.meta.enums %}
|
||||
{{ info.info('Enum Constant Detail', page.meta.enums, 'enum', defaults=page.meta.defaults) }}
|
||||
{% endif %}
|
||||
{% if page.meta.methods %}
|
||||
{{ info.info('Method Detail', page.meta.methods, 'method', defaults=page.meta.defaults)}}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -1,228 +0,0 @@
|
||||
{% macro summary(title, summaries, inherits, details_type = 'unknown', defaults = []) -%}
|
||||
<h2 id="{{ title | lower() | replace(' ', '-') | e}}">
|
||||
{{ title }}
|
||||
</h2>
|
||||
<div class="md-typeset__scrollwrap">
|
||||
<div class="md-typeset__table">
|
||||
{% if summaries %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
{% if details_type and details_type == "method" %}
|
||||
<th>Modifier and Type</th>
|
||||
{% endif %}
|
||||
<th>{{ details_type | capitalize() }}</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for summary in summaries %}
|
||||
<tr>
|
||||
{% set modifiers = namespace(name = "") %}
|
||||
{% for attribute in summary.attributes %}
|
||||
{% set modifiers.name = modifiers.name ~ attribute %}
|
||||
{% if loop.revindex > 0 %}{% set modifiers.name = modifiers.name ~ " " %}{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if modifiers.name %}{% set modifiers.name = modifiers.name ~ " " %}{% endif %}
|
||||
|
||||
{% if summary.type and summary.type.name %}
|
||||
{% set modifiers.name = modifiers.name ~ summary.type.name %}
|
||||
{% elif defaults and defaults.type and defaults.type.name %}
|
||||
{% set modifiers.name = modifiers.name ~ defaults.type.name %}
|
||||
{% else %}
|
||||
{% set modifiers.name = modifiers.name ~ "void" %}
|
||||
{% endif %}
|
||||
|
||||
{% set desc = "" %}
|
||||
{% if summary.deprecated %}
|
||||
{% set desc = "<api__deprecated></api__deprecated><br>" ~ summary.deprecated.split("<br>")[0] %}
|
||||
{% elif summary.description %}
|
||||
{% set desc = summary.description.split("<br>")[0] %}
|
||||
{% endif %}
|
||||
|
||||
{% set final_name = namespace(name = "") %}
|
||||
{% if details_type and details_type == "enum" %}
|
||||
{% set final_name.name = summary.name | upper() %}
|
||||
{% else %}
|
||||
{% set final_name.name = summary.name ~ "(" %}
|
||||
{% endif %}
|
||||
|
||||
{% for parameter in summary.parameters %}
|
||||
{% set final_name.name = final_name.name ~ parameter.type %}
|
||||
{% if loop.revindex > 1 %}{% set final_name.name = final_name.name ~ ", " %}{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if details_type != "enum" %}
|
||||
{% set final_name.name = final_name.name ~ ")" %}
|
||||
{% endif %}
|
||||
|
||||
{% if details_type and details_type == "method" %}
|
||||
{% if modifiers.name %}
|
||||
<td><code>{{ modifiers.name }}</code></td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<td>
|
||||
{% if summary.link %}
|
||||
<a href="{{ summary.link }}"><code>{{ final_name.name | e}}</code></a>
|
||||
{% else %}
|
||||
<a href="#{{ final_name.name | lower() | replace(' ', '-') | e }}"><code>{{ final_name.name | e}}</code></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ desc }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if inherits %}
|
||||
{% for classpath, inherit in inherits.items() %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Methods inherited from <a href="{{ inherit.link }}"><code>{{ classpath }}</code></a></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
{% for inh in inherit.list %}
|
||||
<a href="{{ inherit.link }}#{{ inh | lower() | replace(' ', '-') | e }}">
|
||||
<code>{{- inh | e -}}</code>
|
||||
</a>
|
||||
{%- if loop.revindex > 1 %}, {% endif -%}
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro info(title, details, details_type = 'unknown', defaults = []) -%}
|
||||
<h2 id="{{ title | lower() | replace(' ', '-') | e}}">
|
||||
{{ title }}
|
||||
</h2>
|
||||
{% for detail in details %}
|
||||
<div class="api-detail">
|
||||
{% set final_name = namespace(name = "") %}
|
||||
{% if details_type == "enum" %}
|
||||
{% set final_name.name = detail.name | upper() %}
|
||||
{% else %}
|
||||
{% set final_name.name = detail.name ~ "(" %}
|
||||
{% endif %}
|
||||
|
||||
{% for parameter in detail.parameters %}
|
||||
{% set final_name.name = final_name.name ~ parameter.type %}
|
||||
{% if loop.revindex > 1 %}{% set final_name.name = final_name.name ~ ", " %}{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if details_type != "enum" %}
|
||||
{% set final_name.name = final_name.name ~ ")" %}
|
||||
{% endif %}
|
||||
|
||||
<h3 id="{{ final_name.name | lower() | replace(' ', '-') | e }}">
|
||||
{% if detail.deprecated %}
|
||||
<api__deprecated></api__deprecated>
|
||||
{% endif %}
|
||||
|
||||
{% for attribute in detail.attributes %}
|
||||
<api__{{- attribute -}}></api__{{- attribute -}}>
|
||||
{% endfor %}
|
||||
|
||||
{% if enum %}
|
||||
<api__static></api__static> <api__final></api__final>
|
||||
{% endif %}
|
||||
|
||||
{% if detail.type and details_type == "method" %}
|
||||
{% if detail.type.link %}
|
||||
<a href="{{ detail.type.link }}" class="api-type__{{- detail.type.type | default('primitive') -}}">{{ detail.type.name | default('void') | e }}</a>
|
||||
{% else %}
|
||||
<span class="api-type__{{- detail.type.type | default('primitive') -}}">{{ detail.type.name | default('void') | e }}</span>
|
||||
{% endif %}
|
||||
{% elif defaults and defaults.type and details_type == "method" %}
|
||||
{% if defaults.type.link %}
|
||||
<a href="{{ defaults.type.link }}" class="api-type__{{- defaults.type.type | default('primitive') -}}">{{ defaults.type.name | default('void') | e }}</a>
|
||||
{% else %}
|
||||
<span class="api-type__{{- defaults.type.type | default('primitive') -}}">{{ defaults.type.name | default('void') }}</span>
|
||||
{% endif %}
|
||||
{% elif details_type == "method" %}
|
||||
<span class="api-type__primitive">void</span>
|
||||
{% endif %}
|
||||
<code>{{ final_name.name | e }}</code><a href="#{{ final_name.name | lower() | replace(' ', '-') | e }}" class="headerlink" title="Permanent link">¶</a>
|
||||
</h3>
|
||||
|
||||
{% if detail.deprecated %}
|
||||
<div class="admonition deprecated">
|
||||
<div class="admonition-title">Deprecation Warning</div>
|
||||
<p>
|
||||
{{ detail.deprecated }}
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if detail.description %}
|
||||
<p class="api-detail__description">
|
||||
{{ detail.description }}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if detail.parameters %}
|
||||
<h4 id="{{ detail.name | lower() | replace(' ', '-') | e }}-parameters">
|
||||
Parameters:<a href="#{{ detail.name | lower() | replace(' ', '-') | e }}-parameters" class="headerlink" title="Permanent link">¶</a>
|
||||
</h4>
|
||||
<ul>
|
||||
{% for parameter in detail.parameters %}
|
||||
<li>
|
||||
{% for attribute in parameter.attribute %}
|
||||
<api__{{- attribute -}}></api__{{- attribute -}}>
|
||||
{% endfor %}
|
||||
<code>{{ parameter.type | e }}: {{ parameter.name }}</code>{% if parameter.description %} - {{ parameter.description }}{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if detail.returns %}
|
||||
<h4 id="{{ detail.name | lower() | replace(' ', '-') | e }}-returns">
|
||||
Returns:<a href="#{{ detail.name | lower() | replace(' ', '-') | e }}-returns" class="headerlink" title="Permanent link">¶</a>
|
||||
</h4>
|
||||
<p class="api-detail__description">
|
||||
{{ detail.returns }}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if detail.throws %}
|
||||
<h4 id="{{ detail.name | lower() | replace(' ', '-') | e }}-throws">
|
||||
Throws:<a href="#{{ detail.name | lower() | replace(' ', '-') | e }}-throws" class="headerlink" title="Permanent link">¶</a>
|
||||
</h4>
|
||||
<ul>
|
||||
{% for throw in detail.throws %}
|
||||
<li>
|
||||
<code>{{ throw.name }}</code>{% if throw.description %} - {{ throw.description }}{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if seealso %}
|
||||
<h4 id="{{ detail.name | lower() | replace(' ', '-') | e }}-see-also">
|
||||
See also:<a href="#{{ detail.name | lower() | replace(' ', '-') | e }}-see-also" class="headerlink" title="Permanent link">¶</a>
|
||||
</h4>
|
||||
<ul>
|
||||
{% for see in detail.seealso %}
|
||||
<li>
|
||||
<a href="{{ see.link }}"><code>{{ see.name }}</code></a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,61 @@
|
||||
{% if page.meta.constructors %}
|
||||
## Constructor Details
|
||||
|
||||
{% for constructor in page.meta.constructors %}
|
||||
<div class="api-detail" markdown>
|
||||
|
||||
{% set name = namespace(text = constructor.name ~ "(") %}
|
||||
{% for parameter in constructor.parameters %}
|
||||
{% set name.text = name.text ~ parameter.type %}
|
||||
{% if loop.revindex > 1 %}{% set name.text = name.text ~ ", " %}{% endif %}
|
||||
{% endfor %}
|
||||
{% set name.text = name.text ~ ")" %}
|
||||
|
||||
{% if constructor.deprecated %}
|
||||
{% set name.text = "<api__deprecated></api__deprecated> " ~ name.text %}
|
||||
{% endif %}
|
||||
|
||||
{% for attribute in constructor.attributes %}
|
||||
{% set name.text = "<api__{{- attribute -}}></api__{{- attribute -}}> " ~ name.text %}
|
||||
{% endfor %}
|
||||
|
||||
### {{ name.text | e }} { #{{ constructor.name | lower() | replace(' ', '-') | e }} }
|
||||
|
||||
{% if constructor.deprecated %}
|
||||
/// deprecated | Deprecation Warning
|
||||
{{ constructor.deprecated }}
|
||||
///
|
||||
{% endif %}
|
||||
|
||||
{% if constructor.description %}
|
||||
<p class="api-detail__description">
|
||||
|
||||
{{ constructor.description }}
|
||||
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if constructor.parameters %}
|
||||
#### Parameters: { #{{ constructor.name | lower() | replace(' ', '-') | e }}-parameters }
|
||||
|
||||
{% for parameter in constructor.parameters %}
|
||||
- {% for attribute in parameter.attributes %}<api__{{- attribute -}}></api__{{- attribute -}}>{% endfor %} `{{ parameter.type | e}}: {{ parameter.name }}`{% if parameter.description %} - {{ parameter.description }}{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if constructor.seealso %}
|
||||
#### See also: { #{{ constructor.name | lower() | replace(' ', '-') | e }}-see-also }
|
||||
|
||||
<div class="api-detail__description" markdown>
|
||||
|
||||
{% for see in constructor.seealso %}
|
||||
- [`{{ see.name }}`]({{ see.link }})
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
@@ -0,0 +1,14 @@
|
||||
{% if page.meta.constructors %}
|
||||
## Constructor Summary
|
||||
|
||||
| Constructor | Description |
|
||||
|-------------|-------------|{% for constructor in page.meta.constructors %}
|
||||
| [`{{ constructor.name }}({% for parameter in constructor.parameters -%}
|
||||
{{ parameter.type }}{% if loop.revindex > 1 %}, {% endif %}
|
||||
{%- endfor %})`](#{{ constructor.name | lower() }}) | {% if constructor.deprecated -%}
|
||||
<api__deprecated></api__deprecated> {{ constructor.deprecated.split("\n")[0] }}
|
||||
{%- elif constructor.description -%}
|
||||
{{ constructor.description.split("\n")[0] }}
|
||||
{%- endif %} |
|
||||
{%- endfor %}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,32 @@
|
||||
{% if page.meta.enums %}
|
||||
## Enum Constant Details
|
||||
|
||||
{% for enum in page.meta.enums %}
|
||||
<div class="api-detail" markdown>
|
||||
|
||||
{% set modifiers = namespace(text = "<api__static></api__static> <api__final></api__final> ") %}
|
||||
|
||||
{% if enum.deprecated %}
|
||||
{% set modifiers.text = "<api__deprecated></api__deprecated> " ~ modifiers.text %}
|
||||
{% endif %}
|
||||
|
||||
### {{ modifiers.text }} `{{ enum.name | upper() }}` { #{{ enum.name | lower() | replace(' ', '-') | e }} }
|
||||
|
||||
{% if enum.deprecated %}
|
||||
/// deprecated | Deprecation Warning
|
||||
{{ enum.deprecated }}
|
||||
///
|
||||
{% endif %}
|
||||
|
||||
{% if enum.description %}
|
||||
<div class="api-detail__description" markdown>
|
||||
|
||||
{{ enum.description }}
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
@@ -0,0 +1,8 @@
|
||||
{% if page.meta.enums %}
|
||||
## Enum Constant Summary
|
||||
|
||||
| Enum | Description |
|
||||
|------|-------------|{% for enum in page.meta.enums %}
|
||||
| [`{{ enum.name | upper() }}`](#{{ enum.name | lower() }}) | {{ enum.description.split("\n")[0] }} |
|
||||
{%- endfor %}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,3 @@
|
||||
{% macro render_name(method) -%}
|
||||
{{ method.name }}({% for parameter in method.parameters %}{{ parameter.type }}{% if loop.revindex > 1 %}, {% endif %}{% endfor %})
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,109 @@
|
||||
{% if page.meta.methods %}
|
||||
## Method Details
|
||||
|
||||
{% for method in page.meta.methods %}
|
||||
<div class="api-detail" markdown>
|
||||
|
||||
{% set modifiers = namespace(text = "") %}
|
||||
|
||||
{% if method.deprecated %}
|
||||
{% set modifiers.text = "<api__deprecated></api__deprecated> " %}
|
||||
{% endif %}
|
||||
|
||||
{% for attribute in method.attributes %}
|
||||
{% set modifiers.text = modifiers.text ~ "<api__" ~ attribute ~ "></api__" ~ attribute ~ ">" %}
|
||||
{% if loop.revindex > 0 %}{% set modifiers.text = modifiers.text ~ " " %}{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if modifiers.text %}{% set modifiers.text = modifiers.text ~ " " %}{% endif %}
|
||||
|
||||
{% set method_type = "" %}
|
||||
{% if method.type %}
|
||||
{% if method.type.link %}
|
||||
{% set method_type = '[' ~ method.type.name|default('void')|e ~ '](' ~ method.type.link ~ '){ .api-type__' ~ method.type.type|default('primitive') ~ '} ' %}
|
||||
{% else %}
|
||||
{% set method_type = '<span class="api-type__' ~ method.type.type|default('primitive') ~ '">' ~ method.type.name|default('void')|e ~ '</span> ' %}
|
||||
{% endif %}
|
||||
{% elif page.meta.defaults and page.meta.defaults.type %}
|
||||
{% if page.meta.defaults.type.link %}
|
||||
{% set method_type = '[' ~ page.meta.defaults.type.name|default('void')|e ~ '](' ~ page.meta.defaults.type.link ~ '){ .api-type__' ~ page.meta.defaults.type.type|default('primitive') ~ '} ' %}
|
||||
{% else %}
|
||||
{% set method_type = '<span class="api-type__' ~ page.meta.defaults.type.type|default('primitive') ~ '">' ~ page.meta.defaults.type.name|default('void')|e ~ '</span> ' %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% set method_type = '<span class="api-type__primitive">void</span> ' %}
|
||||
{% endif %}
|
||||
|
||||
{% set name = namespace(text = method.name ~ "(") %}
|
||||
{% for parameter in method.parameters %}
|
||||
{% set name.text = name.text ~ parameter.type %}
|
||||
{% if loop.revindex > 1 %}{% set name.text = name.text ~ ", " %}{% endif %}
|
||||
{% endfor %}
|
||||
{% set name.text = name.text ~ ")" %}
|
||||
|
||||
### {{ modifiers.text }} {{ method_type }} `{{ name.text }}` { #{{ method.name | lower() | replace(' ', '-') | e }} }
|
||||
|
||||
{% if method.deprecated %}
|
||||
/// deprecated | Deprecation Warning
|
||||
{{ method.deprecated }}
|
||||
///
|
||||
{% endif %}
|
||||
|
||||
{% if method.description %}
|
||||
<div class="api-detail__description" markdown>
|
||||
|
||||
{{ method.description }}
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if method.parameters %}
|
||||
#### Parameters: { #{{ method.name | lower() | replace(' ', '-') | e }}-parameters }
|
||||
|
||||
<div class="api-detail__description" markdown>
|
||||
|
||||
{% for parameter in method.parameters %}
|
||||
- {% for attribute in parameter.attributes %}<api__{{- attribute -}}></api__{{- attribute -}}>{% endfor %} `{{ parameter.type | e}}: {{ parameter.name }}`{% if parameter.description %} - {{ parameter.description }}{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if method.returns %}
|
||||
#### Returns: { #{{ method.name | lower() | replace(' ', '-') | e }}-returns }
|
||||
|
||||
<div class="api-detail__description" markdown>
|
||||
|
||||
{{ method.returns }}
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if method.throws %}
|
||||
#### Throws: { #{{ method.name | lower() | replace(' ', '-') | e }}-throws }
|
||||
|
||||
<div class="api-detail__description" markdown>
|
||||
|
||||
{% for throw in method.throws %}
|
||||
- `{{ throw.name }}`{% if throw.description %} - {{ throw.description }}{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if method.seealso %}
|
||||
#### See also: { #{{ method.name | lower() | replace(' ', '-') | e }}-see-also }
|
||||
|
||||
<div class="api-detail__description" markdown>
|
||||
|
||||
{% for see in method.seealso %}
|
||||
- [`{{ see.name }}`]({{ see.link }})
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
@@ -0,0 +1,31 @@
|
||||
{% if page.meta.methods or page.meta.inherits %}
|
||||
## Method Summary
|
||||
|
||||
{% if page.meta.methods %}
|
||||
| Modifier and Type | Method | Description |
|
||||
|-------------------|--------|-------------|{% for method in page.meta.methods %}
|
||||
| `{% if method.attributes -%}
|
||||
{% for attribute in method.attributes -%}
|
||||
{{ attribute }}{% if loop.revindex > 0 %} {% endif %}
|
||||
{%- endfor %} {%- endif %}{% if method.type and method.type.name -%}
|
||||
{{ method.type.name }}
|
||||
{%- elif page.meta.defaults and page.meta.defaults.type and page.meta.defaults.type.name -%}
|
||||
{{ page.meta.defaults.type.name }}
|
||||
{%- else -%}void{%- endif %}` | [`{{ method.name }}({% for parameter in method.parameters -%}
|
||||
{{ parameter.type }}{% if loop.revindex > 1 %}, {% endif %}
|
||||
{%- endfor %})`](#{{ method.name | lower() }}) | {% if method.deprecated -%}
|
||||
<api__deprecated></api__deprecated> {{ method.deprecated.split("\n")[0] }}
|
||||
{%- elif method.description -%}
|
||||
{{ method.description.split("\n")[0] }}
|
||||
{%- endif %} |
|
||||
{%- endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if page.meta.inherits %}
|
||||
{% for classpath, inherit in page.meta.inherits.items() %}
|
||||
| Methods inherited from [`{{ classpath }}`]({{ inherit.link }}) |
|
||||
|----------------------------------------------------------------|
|
||||
| {% for entry in inherit.list -%}[`{{ entry.name | e }}`]({{ inherit.link }}#{{ entry.link | lower() | replace(' ', '-') | e }}){% if loop.revindex > 1 %}, {% endif %}{% endfor %} |
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,11 @@
|
||||
{% if page.meta.classes %}
|
||||
## Nested Class Summary
|
||||
|
||||
| Modifier and Type | Class | Description |
|
||||
|-------------------|-------|-------------|{% for class in page.meta.classes %}
|
||||
| `{% if class.attributes -%}
|
||||
{% for attribute in class.attributes -%}
|
||||
{{ attribute }}{% if loop.revindex > 1 %} {% endif %}
|
||||
{%- endfor %} {%- endif %}{{ class.type }}` | [`{{ class.name }}`]({{ class.link }}) | {{ class.description.split("\n")[0] }} |
|
||||
{%- endfor %}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block announce %}
|
||||
<span class="twemoji balloon">{% include ".icons/material/balloon.svg" %}</span>
|
||||
<span class="rainbow">Celebrating <b>50,000 downloads</b> of AdvancedServerList! THANK YOU!</span>
|
||||
<span class="twemoji balloon">{% include ".icons/material/balloon.svg" %}</span>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,10 @@
|
||||
[discord]: https://discord.gg/MyPTjpsgGH
|
||||
|
||||
[placeholderapi]: https://modrinth.com/plugin/PlaceholderAPI
|
||||
[viaversion]: https://hangar.papermc.io/ViaVersion/ViaVersion
|
||||
[papiproxybridge]: https://hangar.papermc.io/William278/PAPIProxyBridge
|
||||
[maintenance]: https://hangar.papermc.io/kennytv/Maintenance
|
||||
|
||||
[modrinth]: https://modrinth.com/plugin/advancedserverlist
|
||||
[hangar]: https://hangar.papermc.io/Andre_601/AdvancedServerList
|
||||
[codeberg]: https://codeberg.org/Andre601/AdvancedServerList
|
||||
@@ -1,7 +1,14 @@
|
||||
---
|
||||
icon: octicons/stop-24
|
||||
---
|
||||
# BanPlugins Addon
|
||||
|
||||
The BanPlugins Addon was made to provide placeholders for different punishment plugins to use in AdvancedServerList.
|
||||
|
||||
/// info
|
||||
The BanPlugins Addon is available in downloads of AdvancedServerList on [:simple-modrinth: Modrinth](https://modrinth.com/plugin/advancedserverlist) and in [Releases on :simple-codeberg: Codeberg](https://codeberg.org/Andre601/AdvancedServerList/releases).
|
||||
///
|
||||
|
||||
## Placeholders
|
||||
|
||||
/// tab | AdvancedBan
|
||||
@@ -9,8 +16,7 @@ The BanPlugins Addon was made to provide placeholders for different punishment p
|
||||
|
||||
**Supported Platforms:**
|
||||
|
||||
- :fontawesome-solid-paper-plane: Paper
|
||||
- :simple-spigotmc: BungeeCord
|
||||
<!-- icon:paper --> <!-- icon:bungee -->
|
||||
|
||||
| Placeholder | Description |
|
||||
|---------------------------------|------------------------------------------------------------------------------------------|
|
||||
@@ -50,9 +56,7 @@ motd:
|
||||
|
||||
**Supported Platforms:**
|
||||
|
||||
- :fontawesome-solid-paper-plane: Paper
|
||||
- :simple-spigotmc: BungeeCord
|
||||
- :fontawesome-solid-paper-plane: Velocity
|
||||
<!-- icon:all -->
|
||||
|
||||
| Placeholder | Description |
|
||||
|---------------------------------|------------------------------------------------------------------------------------------|
|
||||
@@ -88,9 +92,7 @@ motd:
|
||||
|
||||
**Supported Platforms:**
|
||||
|
||||
- :fontawesome-solid-paper-plane: Paper
|
||||
- :simple-spigotmc: BungeeCord
|
||||
- :fontawesome-solid-paper-plane: Velocity
|
||||
<!-- icon:all -->
|
||||
|
||||
| Placeholder | Description |
|
||||
|------------------------------|------------------------------------------------------------------------------------------|
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
icon: octicons/checklist-24
|
||||
---
|
||||
# LuckPerms Addon
|
||||
|
||||
The LuckPerms addon provides placeholders to return values from LuckPerms for a specific player.
|
||||
This can be a players primary group, whether they have a specific group or whether they have a specific permission.
|
||||
|
||||
/// note
|
||||
The Userdata retrieval is done asynchronously by LuckPerms.
|
||||
However, this addon forces the thread for this to be blocked, which can cause lag, if done on the main thread.
|
||||
|
||||
This was done due to possible downsides and issues with trying to keep it async and block-free.
|
||||
To mitigate possible lag is the retrieved user data cached for 1 minute before being discarded.
|
||||
///
|
||||
|
||||
## Placeholders
|
||||
|
||||
**Supported Platforms:**
|
||||
|
||||
<!-- icon:all -->
|
||||
|
||||
| Placeholder | Description |
|
||||
|-------------------------------------------|-----------------------------------------------------------------------------------------------------------------|
|
||||
| `${luckperms hasGroup <group>}` | Returns whether the player has the specified group or not. |
|
||||
| `${luckperms hasPermission <permission>}` | Returns whether the player has the specified permission.<br/>No permission or one set to `false` returns false. |
|
||||
| `${luckperms primaryGroup}` | Gives the name of the primary group the player has. |
|
||||
|
||||
/// details | Example
|
||||
type: example
|
||||
|
||||
```yaml title="is_admin.yml
|
||||
priority: 1
|
||||
condition: "${luckperms hasGroup admin}"
|
||||
|
||||
motd:
|
||||
- "<yellow>Welcome Admin!"
|
||||
```
|
||||
///
|
||||
+16
-2
@@ -13,16 +13,30 @@ These Addons are made by Andre601 and available together with the plugin downloa
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- [**BanPlugins**](banplugins.md)
|
||||
- [**BanPlugins**](first-party/banplugins.md)
|
||||
|
||||
----
|
||||
|
||||
Addon for different Punishment plugins such as AdvancedBan, that add new placeholders to use in AdvancedServerList.
|
||||
|
||||
- [**LuckPerms**](first-party/luckperms.md)
|
||||
|
||||
----
|
||||
|
||||
Addon for adding LuckPerms-related placeholders to use in conditions.
|
||||
|
||||
</div>
|
||||
|
||||
## Third-party
|
||||
|
||||
Third-party addons made by other people not working on AdvancedServerList.
|
||||
|
||||
*Currently no entries*
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- [**SayanVanish**](third-party/sayanvanish.md)
|
||||
|
||||
----
|
||||
|
||||
SayanVanish adds additional placeholders to the Velocity version of AdvancedServerList to use.
|
||||
|
||||
</div>
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
---
|
||||
icon: octicons/eye-closed-24
|
||||
---
|
||||
# SayanVanish
|
||||
|
||||
The plugin [SayanVanish]{ target="_blank" rel="nofollow" } is hooking into AdvancedServerList to provide placeholders of its own to be used in conditions and text options.
|
||||
|
||||
## Placeholders
|
||||
|
||||
/// tab | :fontawesome-solid-paper-plane: Paper
|
||||
[:octicons-file-code-24: Source][source-paper]
|
||||
|
||||
| Placeholder | Description |
|
||||
|----------------------------------|--------------------------------------------------------------------------------------------|
|
||||
| `${sayanvanish vanished}` | Returns true or false based on if the player is vanished. |
|
||||
| `${sayanvanish level}` | Returns the vanish level of the player. |
|
||||
| `${sayanvanish count}` | Returns the number of online players that are vanished. |
|
||||
| `${sayanvanish online_here}` | Returns the number of online players on the same server that aren't vanished. |
|
||||
| `${sayanvanish online_total}` | Returns the number of online players on the entire network that aren't vanished.^[1](#n1)^ |
|
||||
| `${sayanvanish online_<server>}` | Returns the number of online players on `<server>` that aren't vanished.^[1](#n1)^ |
|
||||
| `${sayanvanish vanish_prefix}` | Returns the prefix for the player while they are vanished or an empty String. |
|
||||
| `${sayanvanish vanish_suffix}` | Returns the suffix for the player while they are vanished or an empty String. |
|
||||
|
||||
<small>^1^{ #n1 } Only works when Proxy mode is enabled and will otherwise return `PROXY_MODE IS NOT ENABLED!`</small>
|
||||
///
|
||||
|
||||
/// tab | :simple-velocity: Velocity
|
||||
[:octicons-file-code-24: Source][source-velocity]
|
||||
|
||||
| Placeholder | Description |
|
||||
|----------------------------------|----------------------------------------------------------------------------------|
|
||||
| `${sayanvanish vanished}` | Returns true or false based on if the player is vanished. |
|
||||
| `${sayanvanish level}` | Returns the vanish level of the player. |
|
||||
| `${sayanvanish count}` | Returns the number of online players that are vanished. |
|
||||
| `${sayanvanish online_total}` | Returns the number of online players on the entire network that aren't vanished. |
|
||||
| `${sayanvanish online_<server>}` | Returns the number of online players on `<server>` that aren't vanished. |
|
||||
| `${sayanvanish vanish_prefix}` | Returns the prefix for the player while they are vanished or an empty String. |
|
||||
| `${sayanvanish vanish_suffix}` | Returns the suffix for the player while they are vanished or an empty String. |
|
||||
///
|
||||
|
||||
[SayanVanish]: https://modrinth.com/plugin/sayanvanish
|
||||
[source-paper]: https://github.com/Syrent/SayanVanish/blob/6e03b874464a7f07c0baba233c94cdfefab5d3e2/sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/hook/FeatureHookAdvancedServerList.kt
|
||||
[source-velocity]: https://github.com/Syrent/SayanVanish/blob/6e03b874464a7f07c0baba233c94cdfefab5d3e2/sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/feature/features/hook/FeatureHookAdvancedServerList.kt
|
||||
@@ -0,0 +1,183 @@
|
||||
---
|
||||
icon: octicons/plus-circle-24
|
||||
---
|
||||
|
||||
AdvancedServerList allows you to add your own Placeholders to be useable through its `${identifier value}` format.
|
||||
The benefit of this method over alternatives such as using PlaceholderAPI placeholders is availability and the fact that these placeholders can be used within the [`condition` option](../profiles/options/condition.md) of the plugin.
|
||||
|
||||
Adding your own placeholder is relatively easy and only requires the main API module to be used in your dependencies, unless you need platform-specific values (Explained later on).
|
||||
|
||||
## Create your PlaceholderProvider class
|
||||
|
||||
AdvancedServerList's placeholder system uses a dedicated [`PlaceholderProvider`](reference/api/ch.andre601.advancedserverlist.api/placeholderprovider.md) class where the placeholder gets defined and its values parsed.
|
||||
In order to add your own, you'll need to first create a new class and make it extend the `PlaceholderProvider` class:
|
||||
|
||||
```java title="MyPlaceholder.java"
|
||||
public class MyPlaceholder extends PlaceholderProvider {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Your IDE should now report issues, namely a missing method to override and a missing Constructor to match the extended class' one. Your IDE should allow you to easily implement those, if not, then manually add the constructor with a `String` argument and the `public String parsePlaceholder` method with `String`, [`GenericPlayer`](reference/api/ch.andre601.advancedserverlist.api/objects/genericplayer.md) and [`GenericServer`](reference/api/ch.andre601.advancedserverlist.api/objects/genericserver.md) arguments:
|
||||
|
||||
```java { title="MyPlaceholder.java" .annotate }
|
||||
public class MyPlaceholder extends PlaceholderProvider {
|
||||
|
||||
public MyPlaceholder(String identifier) {
|
||||
super(identifier); // (1)
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
1. It is recommended to replace the Constructor with a No-Args Constructor and set the placeholder name directly in the `super` call:
|
||||
```java
|
||||
public MyPlaceholder() {
|
||||
super("myplaceholder");
|
||||
}
|
||||
```
|
||||
|
||||
## Parsing placeholders
|
||||
|
||||
To now parse your placeholders, all you need to do is edit the `parsePlaceholder` method.
|
||||
AdvancedServerList calls this method whenever it finds a matching identifier of a PlaceholderProvider, with the `String` argument being the actual value after the identifier and the `GenericPlayer` and `GenericServer` arguments holding the Player and server values respectively.
|
||||
|
||||
As an example, assuming you simply want to return `Hello` for the placeholder `${myplaceholder sayHello}`, this would be the code for it:
|
||||
|
||||
```java { title="MyPlaceholder.java" .annotate }
|
||||
public class MyPlaceholder extends PlaceholderProvider {
|
||||
|
||||
public MyPlaceholder() {
|
||||
super("myplaceholder");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server) {
|
||||
if (placeholder.equals("sayHello")) {
|
||||
return "Hello";
|
||||
}
|
||||
|
||||
return null; // (1)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
1. Returning `null` tells AdvancedServerList that the placeholder was invalid, making it return the placeholder unchanged, while also reporting an error, if used in `condition`.
|
||||
|
||||
## Registering the Placeholder
|
||||
|
||||
The final step now would be to register your placeholder for AdvancedServerList to use.
|
||||
To do this, obtain an instance of the [`AdvancedServerListAPI`](reference/api/ch.andre601.advancedserverlist.api/advancedserverlistapi.md) through its static `get()` method and then call `addPlaceholderProvider(PlaceholderProvider)` through it:
|
||||
|
||||
/// tab | :fontawesome-solid-paper-plane: Paper Example
|
||||
```java title="MyPlugin.java"
|
||||
public class MyPlugin extends JavaPlugin {
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
AdvancedServerListAPI api = AdvancedServerListAPI.get();
|
||||
api.addPlaceholderProvider(new MyPlaceholder());
|
||||
getLogger().info("Hooked into AdvancedServerList!");
|
||||
}
|
||||
}
|
||||
```
|
||||
///
|
||||
|
||||
/// tab | :octicons-git-merge-24: BungeeCord Example
|
||||
```java title="MyPlugin.java"
|
||||
public class MyPlugin extends Plugin {
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
AdvancedServerListAPI api = AdvancedServerListAPI.get();
|
||||
api.addPlaceholderProvider(new MyPlaceholder());
|
||||
getLogger().info("Hooked into AdvancedServerList!");
|
||||
}
|
||||
}
|
||||
```
|
||||
///
|
||||
|
||||
/// tab | :simple-velocity: Velocity Example
|
||||
```java title="MyPlugin.java"
|
||||
@Plugin(
|
||||
id = "myplugin",
|
||||
version = "1.0.0",
|
||||
authors = {"You?"},
|
||||
dependencies = {
|
||||
@Dependency(id = "advancedserverlist")
|
||||
}
|
||||
)
|
||||
public class MyPlugin {
|
||||
|
||||
private final ProxyServer proxy;
|
||||
private final Logger logger;
|
||||
|
||||
@Inject
|
||||
public MyPlugin(ProxyServer proxy, Logger logger) {
|
||||
this.proxy = proxy;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onProxyInitialization(ProxyInitializeEvent event) {
|
||||
AdvancedServerListAPI api = AdvancedServerListAPI.get();
|
||||
api.addPlaceholderProvider(new MyPlaceholder());
|
||||
logger.info("Hooked into AdvancedServerList!");
|
||||
}
|
||||
}
|
||||
```
|
||||
///
|
||||
|
||||
## You're done
|
||||
|
||||
Your Placeholders should now be registered successfully if they check the following:
|
||||
|
||||
- The provided identifier is not `null` nor empty.
|
||||
- The provided identifier does not contain spaces.
|
||||
- The provided identifier is not already registered in the API.
|
||||
|
||||
## Note on `GenericPlayer` and `GenericServer`
|
||||
|
||||
The different platform APIs of AdvancedServerList provide extended versions of the `GenericPlayer` and `GenericServer` instances used, providing additional methods otherwise not available.
|
||||
Should you want to use these specific instances will you need to add the platform API as an additional dependency to your project and then cast the `GenericPlayer` and/or `GenericServer` instance to the platform specific version.
|
||||
|
||||
Below is an example using the `platform-bukkit` API (This assumes your plugin is one running on Paper):
|
||||
|
||||
```java { title="MyPlaceholder.java" .annotate }
|
||||
public class MyPlaceholder extends PlaceholderProvider {
|
||||
|
||||
public MyPlaceholder() {
|
||||
super("myplaceholder");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server) {
|
||||
if (!(player instanceof BukkitPlayer bukkitPlayer)) {
|
||||
return null; // (1)
|
||||
}
|
||||
|
||||
return switch (placeholder) {
|
||||
case "playedBefore" -> String.valueOf(bukkitPlayer.hasPlayedBefore());
|
||||
case "whitelisted" -> String.valueOf(bukkitPlayer.isWhitelisted());
|
||||
case "banned" -> String.valueOf(bukkitPlayer.isBanned());
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
1. Depending on the Java version you use will you need to alter the code:
|
||||
```java
|
||||
if (!(player instanceof BukkitPlayer)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// You need to cast it separately.
|
||||
BukkitPlayer bukkitPlayer = (BukkitPlayer) player;
|
||||
|
||||
// ...use bukkitPlayer here.
|
||||
```
|
||||
+26
-20
@@ -7,6 +7,15 @@ icon: octicons/log-24
|
||||
This page lists the recent changes made towards the AdvancedServerListAPI.
|
||||
They are ordered newest to oldest.
|
||||
|
||||
## v4.1.0 { #v4.1.0 }
|
||||
|
||||
### :octicons-plus-circle-24:{ .changelog-added title="Added" } Added { #v-added }
|
||||
|
||||
- New setter method added to [`ProfileEntry.Builder`][profileentry.builder] class:
|
||||
- [`hidePlayersHoverEnabled(NullBool)`][hidePlayersHoverEnabled]
|
||||
|
||||
[hidePlayersHoverEnabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#hideplayershoverenabled
|
||||
|
||||
## v4.0.0 { #v4.0.0 }
|
||||
|
||||
### :octicons-alert-24:{ .changelog-breaking title="Breaking Changes" } Breaking Changes { #v4.0.0-breaking }
|
||||
@@ -17,12 +26,12 @@ They are ordered newest to oldest.
|
||||
|
||||
### :octicons-plus-circle-24:{ .changelog-added title="Added" } Added { #v4.0.0-added }
|
||||
|
||||
- New setter methods added to [`ProfileEntry.Builder`][profilentry.builder] class:
|
||||
- New setter methods added to [`ProfileEntry.Builder`][profileentry.builder] class:
|
||||
- [`onlinePlayersCount(String)`][onlinePlayersCount]
|
||||
- [`onlinePlayersEnabled(NullBool)`][onlinePlayersEnabled]
|
||||
|
||||
[onlinePlayersCount]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#onlineplayerscount(integer)
|
||||
[onlinePlayersEnabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#onlineplayersenabled(nullbool)
|
||||
[onlinePlayersCount]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#onlineplayerscount
|
||||
[onlinePlayersEnabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#onlineplayersenabled
|
||||
|
||||
## v3.3.0 { #v3.3.0 }
|
||||
|
||||
@@ -53,15 +62,15 @@ They are ordered newest to oldest.
|
||||
- [`playerCountText(String)`][playercounttext]
|
||||
- [`players(List<String>)`][players]
|
||||
|
||||
[extraplayerscount]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#extraplayerscount(integer)
|
||||
[extraplayersenabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#extraplayersenabled(nullbool)
|
||||
[favicon]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#favicon(string)
|
||||
[hideplayersenabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#hideplayersenabled(nullbool)
|
||||
[maxplayerscount]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#maxplayerscount(integer)
|
||||
[maxplayersenabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#maxplayersenabled(nullbool)
|
||||
[motd]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#motd(list<string>)
|
||||
[playercounttext]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#playercounttext(string)
|
||||
[players]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#players(list<string>)
|
||||
[extraplayerscount]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#extraplayerscount
|
||||
[extraplayersenabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#extraplayersenabled
|
||||
[favicon]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#favicon
|
||||
[hideplayersenabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#hideplayersenabled
|
||||
[maxplayerscount]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#maxplayerscount
|
||||
[maxplayersenabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#maxplayersenabled
|
||||
[motd]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#motd
|
||||
[playercounttext]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#playercounttext
|
||||
[players]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#players
|
||||
|
||||
### :octicons-pencil-24:{ .changelog-changed title="Changed" } Changed { #v3.2.0-changed }
|
||||
|
||||
@@ -119,22 +128,19 @@ They are ordered newest to oldest.
|
||||
- [`maxPlayersEnabled()`][maxplayersenabled] - Returns the value for `playerCount -> maxPlayers -> enabled` as `NullBool`
|
||||
- [`maxPlayersCount()`][maxplayerscount] - Returns the value of `playerCount -> maxPlayers -> amount` as nullable `Integer`
|
||||
- New setters in [`ProfileEntry.Builder`][profileentry.builder]:
|
||||
- [`setMaxPlayersEnabled(NullBool)`][setmaxplayersenabled] - Sets whether the max players feature is enabled or not.
|
||||
- [`setMaxPlayersCount(Integer)`][setmaxplayerscount] - Sets the number to use for the max players count.
|
||||
- [`setExtraPlayersCount(Integer)`][setextraplayersecount] - Replacement for [deprecated `setExtraPlayerCount(Integer)` method](#v3.1.0-deprecated).
|
||||
- `setMaxPlayersEnabled(NullBool)`- Sets whether the max players feature is enabled or not.
|
||||
- `setMaxPlayersCount(Integer)` - Sets the number to use for the max players count.
|
||||
- `setExtraPlayersCount(Integer)` - Replacement for [deprecated `setExtraPlayerCount(Integer)` method](#v3.1.0-deprecated).
|
||||
|
||||
### :octicons-zap-24:{ .changelog-breaking title="Deprecated" } Deprecated { #v3.1.0-deprecated }
|
||||
|
||||
- `setExtraPlayerEnabled(Integer)` in `ProfileEntry.Builder` - Typo in method name.
|
||||
|
||||
[profileentry]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/index.md
|
||||
[maxplayersenabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/index.md#maxplayersenabled()
|
||||
[maxplayerscount]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/index.md#maxplayerscount()
|
||||
[maxplayersenabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/index.md#maxplayersenabled
|
||||
[maxplayerscount]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/index.md#maxplayerscount
|
||||
|
||||
[profileentry.builder]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md
|
||||
[setmaxplayersenabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#setmaxplayersenabled(nullbool)
|
||||
[setmaxplayerscount]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#setmaxplayerscount(integer)
|
||||
[setextraplayersecount]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#setextraplayerscount(integer)
|
||||
|
||||
----
|
||||
|
||||
|
||||
+12
-166
@@ -11,7 +11,7 @@ AdvancedServerList offers an API that other plugins can use to add their own pla
|
||||
Add the following to your `pom.xml`, `build.gradle` or `build.gradle.kts` depending on what you use:
|
||||
|
||||
/// tab | :simple-apachemaven: Maven
|
||||
```xml title="pom.xml"
|
||||
```xml { data-md-component="api-version" title="pom.xml" }
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>codeberg</id>
|
||||
@@ -51,7 +51,7 @@ Add the following to your `pom.xml`, `build.gradle` or `build.gradle.kts` depend
|
||||
///
|
||||
|
||||
/// tab | :simple-gradle: Gradle
|
||||
```groovy title="build.gradle"
|
||||
```groovy { data-md-component="api-version" title="build.gradle" }
|
||||
repositorories {
|
||||
maven { url = 'https://codeberg.org/api/packages/Andre601/maven/' }
|
||||
}
|
||||
@@ -68,7 +68,7 @@ dependencies {
|
||||
///
|
||||
|
||||
/// tab | :simple-gradle: Gradle (KTS)
|
||||
```kotlin title="build.gradle.kts"
|
||||
```kotlin { data-md-component="api-version" title="build.gradle.kts" }
|
||||
repositories {
|
||||
maven("https://codeberg.org/api/packages/Andre601/maven/")
|
||||
}
|
||||
@@ -84,129 +84,21 @@ dependencies {
|
||||
```
|
||||
///
|
||||
|
||||
## Adding your own Placeholders
|
||||
## Tutorials
|
||||
|
||||
<!-- admo:info This requires the main api module -->
|
||||
Dedicated pages to explain aspects of the API are available:
|
||||
|
||||
The API allows you to add your own placeholders which would be available through the `${<identifier> <values>}` placeholder Pattern in a Server List profile.
|
||||
|
||||
To add your own placeholders, follow these steps:
|
||||
|
||||
### 1. Create a Placeholder class
|
||||
|
||||
You should first create a new class and make it extend the abstract [`PlaceholderProvider`][placeholderprovider]. This class includes a constructor and a method you need to add, so do that:
|
||||
|
||||
```java
|
||||
public class MyPlaceholders extends PlaceholderProvider {
|
||||
|
||||
public MyPlaceholders(String identifier) {
|
||||
super(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
/// tip
|
||||
You can replace the Constructor with a String argument with a no-args constructor and set the identifier directly in the `super`:
|
||||
```java
|
||||
public MyPlaceholders() {
|
||||
super("myplaceholders");
|
||||
}
|
||||
```
|
||||
/// html | div.grid.cards
|
||||
- [:octicons-plus-circle-24: Adding Placeholders](adding-placeholders.md)
|
||||
- [:octicons-broadcast-24: Listening for Events](listening-for-events.md)
|
||||
///
|
||||
|
||||
The [`parsePlaceholder(String, GenericPlayer, GenericServer)`][parseplaceholder] method is used by AdvancedServerList to replace a matching placeholder with a value.
|
||||
What you return is completely up to you. Just keep in mind that returning `null` will be treated as an invalid placeholder by AdvancedServerList, resulting in the placeholder being returned as-is without any changes.
|
||||
|
||||
[placeholderprovider]: reference/api/ch.andre601.advancedserverlist.api/placeholderprovider.md
|
||||
[parseplaceholder]: reference/api/ch.andre601.advancedserverlist.api/placeholderprovider.md#parseplaceholder(string,-genericplayer,-genericserver)
|
||||
|
||||
/// details | Example of final class
|
||||
type: example
|
||||
|
||||
```java
|
||||
public class MyPlaceholders extends PlaceholderProvider {
|
||||
|
||||
public MyPlaceholders() {
|
||||
super("myplaceholders");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server) {
|
||||
if(placeholder.equalsIgnoreCase("hello"))
|
||||
return "Hello " + player.getName();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
///
|
||||
|
||||
### 2. Register the Placeholders
|
||||
|
||||
Next step should be to register the placeholder. To achieve this, first obtain an instance of the [`AdvancedServerListAPI`][advancedserverlistapi] by using the static [`get()`][get] method. After that call [`addPlaceholderProvider(PlaceholderProvider placeholderProvider)`][addplaceholderprovider].
|
||||
Your code may look similar to this:
|
||||
|
||||
```java title="Registering PlaceholderProvider class"
|
||||
public class MyPlugin extends JavaPlugin {
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
AdvancedServerListAPI api = AdvancedServerListAPI.get();
|
||||
|
||||
api.addPlaceholderProvider(new MyPlaceholders());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
[advancedserverlistapi]: reference/api/ch.andre601.advancedserverlist.api/advancedserverlistapi.md
|
||||
[get]: reference/api/ch.andre601.advancedserverlist.api/advancedserverlistapi.md#get()
|
||||
[addplaceholderprovider]: reference/api/ch.andre601.advancedserverlist.api/advancedserverlistapi.md#addplaceholderprovider(placeholderprovider)
|
||||
|
||||
This should register your PlaceholderExpansion as long as it is valid, meaning that the identifier...
|
||||
|
||||
- ...is not `null`
|
||||
- ...is not containing spaces
|
||||
- ...is not using a name already registered in the API
|
||||
|
||||
### 3. Declare AdvancedServerList as (soft)depend
|
||||
## Declare AdvancedServerList as (soft)depend
|
||||
|
||||
The final thing you should make sure is to define AdvancedServerList as a depend or soft-depend for your plugin, to make sure it loads after AdvancedServerList.
|
||||
|
||||
Below are example setups for Spigot, Paper, BungeeCord and Velocity:
|
||||
|
||||
/// tab | :simple-spigotmc: Spigot
|
||||
//// tab | Softdepend
|
||||
```yaml title="plugin.yml"
|
||||
name: "MyPlugin"
|
||||
author: "author"
|
||||
version: "1.0.0"
|
||||
|
||||
main: "com.example.plugin.ExamplePlugin"
|
||||
|
||||
softdepend:
|
||||
- AdvancedServerList
|
||||
```
|
||||
////
|
||||
|
||||
//// tab | Depend
|
||||
```yaml title="plugin.yml"
|
||||
name: "MyPlugin"
|
||||
author: "author"
|
||||
version: "1.0.0"
|
||||
|
||||
main: "com.example.plugin.ExamplePlugin"
|
||||
|
||||
depend:
|
||||
- AdvancedServerList
|
||||
```
|
||||
////
|
||||
///
|
||||
|
||||
/// tab | :fontawesome-solid-paper-plane: Paper
|
||||
//// tab | Softdepend
|
||||
```yaml title="paper-plugin.yml"
|
||||
@@ -241,7 +133,7 @@ dependencies:
|
||||
////
|
||||
///
|
||||
|
||||
/// tab | :octicons-git-merge-24: BungeeCord
|
||||
/// tab | :simple-spigotmc: BungeeCord
|
||||
//// tab | Softdepend
|
||||
```yaml title="bungee.yml"
|
||||
name: "MyPlugin"
|
||||
@@ -269,7 +161,7 @@ depends:
|
||||
////
|
||||
///
|
||||
|
||||
/// tab | :octicons-git-merge-24: Velocity
|
||||
/// tab | :simple-velocity: Velocity
|
||||
//// tab | Softdepend (File)
|
||||
```json title="velocity-plugin.json"
|
||||
{
|
||||
@@ -357,50 +249,4 @@ public class MyPlugin {
|
||||
|
||||
### 4. You're done!
|
||||
|
||||
Your plugin should now hook into AdvancedServerList and register its own custom placeholders to use.
|
||||
|
||||
## Listening for events
|
||||
|
||||
<!-- admo:info These events require the platform-specific APIs -->
|
||||
|
||||
AdvancedServerList provides Events that your plugin can listen for specific situations.
|
||||
|
||||
### PreServerListSetEvent
|
||||
|
||||
This event is called **before** AdvancedServerList starts handling the PingEvent of the Server/Proxy.
|
||||
It allows you to get and alter the [ProfileEntry][profileentry] used in the PingEvent, or even outright cancel the event itself.
|
||||
|
||||
The event offers the following methods:
|
||||
|
||||
- [`getEntry()`][getentry] - Gets the currently set [`ProfileEntry`][profileentry].
|
||||
- [`setEntry(ProfileEntry)`][setentry] - Sets a new [`ProfileEntry`][profileentry] to use.
|
||||
- [`isCancelled()`][iscancelled] - Gets the current cancelled state of the event.
|
||||
- [`setCancelled(boolean)`][setcancelled] - Sets the current cancelled state.
|
||||
|
||||
Please check the server/proxy's documentation on how to listen for events with your plugin.
|
||||
|
||||
[getentry]: reference/api/ch.andre601.advancedserverlist.api/events/genericserverlistevent.md#getentry()
|
||||
[setentry]: reference/api/ch.andre601.advancedserverlist.api/events/genericserverlistevent.md#setentry(profileentry)
|
||||
[iscancelled]: reference/api/ch.andre601.advancedserverlist.api/events/genericserverlistevent.md#iscancelled()
|
||||
[setcancelled]: reference/api/ch.andre601.advancedserverlist.api/events/genericserverlistevent.md#setcancelled(boolean)
|
||||
|
||||
### PostServerListSetEvent
|
||||
|
||||
This event is called **after** AdvancedServerList has completed its handling of the Server's/Proxy's PingEvent.
|
||||
|
||||
It provides a method to retrieve the [ProfileEntry][profileentry] used during the PingEvent.
|
||||
Do note that this ProfileEntry is **not** a representation of what is actually displayed in the server list, but rather what has been used by AdvancedServerList. It may also be `null` should the event handling get cancelled for any reason.
|
||||
|
||||
[profileentry]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/index.md
|
||||
|
||||
### ProfileEntry
|
||||
|
||||
The ProfileEntry record is the core class used within the `PreServerListSetEvent`. It sets what values (MOTD, Favicon, Players, etc.) should be displayed.
|
||||
Note that the record is - by nature - immutable and that the [`Builder`][builder] class should be used to create a new ProfileEntry instance to use.
|
||||
|
||||
A convenience method ([`builder()`][builder()]) exists to create a `Builder` instance from an existing `ProfileEntry` in case you only want to alter certain options while everything else should remain unchanged.
|
||||
|
||||
This allows you to customize the Server list using your plugin. Just keep in mind that other plugins may also do the same, overriding your changes.
|
||||
|
||||
[builder]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md
|
||||
[builder()]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/index.md#builder()
|
||||
Your plugin should now hook into AdvancedServerList and register its own custom placeholders to use.
|
||||
@@ -0,0 +1,170 @@
|
||||
---
|
||||
icon: octicons/broadcast-24
|
||||
---
|
||||
|
||||
AdvancedServerList provides some events that your plugin can listen to.
|
||||
These events are structurally the same across all platforms, yet still require the usage of Platform-specific APIs due to how some events are registered and handled on some platforms.
|
||||
|
||||
The plugin currently offers the following events:
|
||||
|
||||
/// html | div.grid.cards
|
||||
- [PreServerListSetEvent](#preserverlistsetevent)
|
||||
- [PostServerListSetEvent](#postserverlistsetevent)
|
||||
///
|
||||
|
||||
## PreServerListSetEvent
|
||||
|
||||
This event is executed before AdvancedServerList tries to modify the player list.
|
||||
|
||||
### Methods { #preserverlistsetevent-methods }
|
||||
|
||||
The following methods are available to use:
|
||||
|
||||
| Method | Description |
|
||||
|-----------------------------------------|--------------------------------------------------------------------------------|
|
||||
| [`getEntry()`][getentry] | Returns the [`ProfileEntry`][profileentry] that AdvancedServerList should use. |
|
||||
| [`setEntry(ProfileEntry)`][setentry] | Sets the [`ProfileEntry`][profileentry] that should be used. |
|
||||
| [`isCancelled()`][iscancelled] | Returns wether this event was cancelled or not. |
|
||||
| [`setCancelled(boolean)`][setcancelled] | Sets the event's cancelled state. |
|
||||
|
||||
[setentry]: reference/api/ch.andre601.advancedserverlist.api/events/genericserverlistevent.md#setentry
|
||||
[iscancelled]: reference/api/ch.andre601.advancedserverlist.api/events/genericserverlistevent.md#iscancelled
|
||||
[setcancelled]: reference/api/ch.andre601.advancedserverlist.api/events/genericserverlistevent.md#setcancelled
|
||||
|
||||
### Notes { #preserverlistsetevent-notes }
|
||||
|
||||
- Providing `null` as the `ProfileEntry` for `setEntry(ProfileEntry)` is not allowed. Also, a copy of the provided `ProfileEntry` is made and used.
|
||||
|
||||
## PostServerListSetEvent
|
||||
|
||||
This event is executed after AdvancedServerList completed the server list modifications.
|
||||
|
||||
### Methods { #postserverlistsetevent-methods }
|
||||
|
||||
| Method | Description |
|
||||
|--------------------------|--------------------------------------------------------------------------|
|
||||
| [`getEntry()`][getentry] | Returns the [`ProfileEntry`][profileentry] that AdvancedServerList used. |
|
||||
|
||||
[getentry]: reference/api/ch.andre601.advancedserverlist.api/events/genericserverlistevent.md#getentry
|
||||
|
||||
### Notes { #postserverlistsetevent-notes }
|
||||
|
||||
- The returned `ProfileEntry` is not an actual representation of what is being displayed to the player and rather an instance of values the plugin received.
|
||||
The returned value may also be `null` should the Ping Event be cancelled due to things such as an invalid Protocol or the [`PreServerListSetEvent`](#preserverlistsetevent) being cancelled.
|
||||
|
||||
## Registering the Events
|
||||
|
||||
/// tab | :fontawesome-solid-paper-plane: Paper
|
||||
```java { title="MyPlugin.java" .annotate }
|
||||
public class MyPlugin extends JavaPlugin {
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
// (1)
|
||||
Bukkit.getPluginManager().registerEvents(new ASLEventListener(), this);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
1. `getServer()` from the `JavaPlugin` class can be used here too instead of the `Bukkit` singleton.
|
||||
|
||||
```java { title="ASLEventListener.java" }
|
||||
public class ASLEventListener implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onPreServerListSet(PreServerListSetEvent event) {
|
||||
System.out.println("PreServerListSetEvent fired!");
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPostServerListSetEvent(PostServerListSetEvent event) {
|
||||
System.out.println("PostServerListSetEvent fired!");
|
||||
}
|
||||
}
|
||||
```
|
||||
///
|
||||
|
||||
/// tab | :simple-spigotmc: BungeeCord
|
||||
```java { title="MyPlugin.java" }
|
||||
public class MyPlugin extends Plugin {
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
getProxy().getPluginManager().registerListener(this, new ASLEventListener());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java { title="ASLEventListener.java" }
|
||||
public class ASLEventListener implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onPreServerListSet(PreServerListSetEvent event) {
|
||||
System.out.println("PreServerListSetEvent fired!");
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPostServerListSetEvent(PostServerListSetEvent event) {
|
||||
System.out.println("PostServerListSetEvent fired!");
|
||||
}
|
||||
}
|
||||
```
|
||||
///
|
||||
|
||||
/// tab | :simple-velocity: Velocity
|
||||
```java { title="MyPlugin.java" }
|
||||
@Plugin(
|
||||
id = "myplugin",
|
||||
name = "MyPlugin",
|
||||
version = "1.0.0",
|
||||
authors = {"author"},
|
||||
dependencies = {
|
||||
@Dependency(
|
||||
id = "advancedserverlist",
|
||||
)
|
||||
}
|
||||
)
|
||||
public class MyPlugin {
|
||||
|
||||
private final ProxyServer proxy;
|
||||
|
||||
@Inject
|
||||
public MyPlugin(ProxyServer server) {
|
||||
this.proxy = proxy;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void init(ProxyInitializeEvent event) {
|
||||
proxy.getEventManager().register(this, new ASLEventListener());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java { title="ASLEventListener.java" }
|
||||
public class ASLEventListener {
|
||||
|
||||
@Subscribe
|
||||
public void onPreServerListSet(PreServerListSetEvent event) {
|
||||
System.out.println("PreServerListSetEvent fired!");
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onPostServerListSetEvent(PostServerListSetEvent event) {
|
||||
System.out.println("PostServerListSetEvent fired!");
|
||||
}
|
||||
}
|
||||
```
|
||||
///
|
||||
|
||||
## What is a ProfileEntry?
|
||||
|
||||
Both events provide and use a so called [`ProfileEntry`][profileentry].
|
||||
The `ProfileEntry` is a record, representing the values of a [Server List Profile](../profiles/index.md), meaning all values you can set in the profile configuration (Except condition and priority) will be available as a `ProfileEntry`.
|
||||
|
||||
It is important to note, that the `ProfileEntry` can be a representation of the [`profiles` option](../profiles/index.md#profiles), the global options or a mixture of both.
|
||||
If you want to create your own instance, you'll need to use the [`ProfileEntry.Builder` class](reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md) as the `ProfileEntry` is unmodifiable.
|
||||
|
||||
A convenience method in the form of [`builder()`][builder] exists to create a Builder instance of the ProfileEntry with its values already pre-set.
|
||||
|
||||
[profileentry]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/index.md
|
||||
[builder]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/index.md#builder
|
||||
+16
-16
@@ -1,10 +1,10 @@
|
||||
---
|
||||
template: api-doc.html
|
||||
api: true
|
||||
|
||||
methods:
|
||||
- name: 'get'
|
||||
description: |
|
||||
Retrieve the instance used of this API.<br>
|
||||
Retrieve the instance used of this API.
|
||||
If no instance has been made so far will a new one be created.
|
||||
returns: 'Instance of this API.'
|
||||
attributes:
|
||||
@@ -14,38 +14,38 @@ methods:
|
||||
type: 'object'
|
||||
- name: 'addPlaceholderProvider'
|
||||
description: |
|
||||
Adds the provided <a href="./../placeholderprovider/"><code>PlaceholderProvider</code></a> to the list, if it passes the following checks:<br>
|
||||
<ul>
|
||||
<li>Provided PlaceholderProvider instance is not null.</li>
|
||||
<li>The identifier is not null nor empty.</li>
|
||||
<li>The identifier does not contain any spaces.</li>
|
||||
<li>A PlaceholderProvider with the same identifier doesn't exist already.</li>
|
||||
</ul>
|
||||
Not passing any of the above checks results in a <a href="./../exceptions/invalidplaceholderproviderexception/"><code>InvalidPlaceholderProviderException</code></a> being thrown.
|
||||
Adds the provided [`PlaceholderAPIProvider`](placeholderprovider.md) to the list, if it passes the following checks:
|
||||
|
||||
- Provided PlaceholderProvider instance is not null.
|
||||
- The identifier is not null nor empty.
|
||||
- The identifier does not contain any spaces.
|
||||
- A PlaceholderProvider with the same identifier doesn't exist already.
|
||||
|
||||
Not passing any of the above checks results in a [`InvalidPlaceholderProviderException`](exceptions/invalidplaceholderproviderexception.md) being thrown.
|
||||
parameters:
|
||||
- name: 'placeholderProvider'
|
||||
description: 'The <a href="./../placeholderprovider/"><code>PlaceholderProvider</code></a> to add.'
|
||||
description: 'The [`PlaceholderProvider`](placeholderprovider.md) to add.'
|
||||
type: PlaceholderProvider
|
||||
attributes:
|
||||
- notnull
|
||||
throws:
|
||||
- name: 'InvalidPlaceholderProviderException'
|
||||
description: 'When the provided <a href="./../placeholderprovider/"><code>PlaceholderProvider</code> instance</a> is null, has a null or empty identifier, the identifier contains spaces, or another PlaceholderProvider with the same identifier is already in use.'
|
||||
description: 'When the provided [`PlaceholderProvider`](placeholderprovider.md) is null, has a null or empty identifier, the identifier contains spaces, or another PlaceholderProvider with the same identifier is already in use.'
|
||||
type:
|
||||
name: 'void'
|
||||
- name: 'retrievePlaceholderProvider'
|
||||
description: 'Retrieves the <a href="./../placeholderprovider/"><code>PlaceholderProvider</code></a> associated with the provided identifier, or <code>null</code> should no such entry exist.'
|
||||
description: 'Retrieves the [`PlaceholderProvider`](placeholderprovider.md) associated with the provided identifier, or `null` should no such entry exist.'
|
||||
parameters:
|
||||
- name: 'identifier'
|
||||
description: 'The identifier to find a matching <a href="./../placeholderprovider/"><code>PlaceholderProvider</code></a> for.'
|
||||
description: 'The identifier to find a matching [`PlaceholderProvider`](placeholderprovider.md) for.'
|
||||
type: String
|
||||
returns: 'Possibly-null <a href="./../placeholderprovider/"><code>PlaceholderProvider</code> instance</a>.'
|
||||
returns: 'Possibly-null [`PlaceholderProvider` instance](placeholderprovider.md).'
|
||||
attributes:
|
||||
- nullable
|
||||
type:
|
||||
name: PlaceholderProvider
|
||||
type: object
|
||||
link: './../placeholderprovider'
|
||||
link: 'placeholderprovider.md'
|
||||
---
|
||||
|
||||
# <api__class></api__class> AdvancedServerListAPI
|
||||
|
||||
+7
-7
@@ -1,27 +1,27 @@
|
||||
---
|
||||
template: api-doc.html
|
||||
api: true
|
||||
|
||||
methods:
|
||||
- name: 'getEntry'
|
||||
description: 'Gets the <a href="../../profiles/profileentry/"><code>ProfileEntry</code></a> currently set.'
|
||||
description: 'Gets the [`ProfileEntry`](../profiles/profileentry/index.md) currently set.'
|
||||
returns: The currently used ProfileEntry.
|
||||
type:
|
||||
name: ProfileEntry
|
||||
type: object
|
||||
link: '../../profiles/profileentry/'
|
||||
link: '../profiles/profileentry/index.md'
|
||||
- name: 'setEntry'
|
||||
description: |-
|
||||
Sets the new <a href="../../profiles/profileentry/"><code>ProfileEntry</code></a> to use.<br>
|
||||
This may not be <code>null</code>.
|
||||
Sets the new [`ProfileEntry`](../profiles/profileentry/index.md) to use.
|
||||
This may not be `null`.
|
||||
parameters:
|
||||
- name: 'entry'
|
||||
description: 'The new <a href="../../profiles/profileentry/"><code>ProfileEntry</code></a> to use.'
|
||||
description: 'The new [`ProfileEntry`](../profiles/profileentry/index.md) to use.'
|
||||
type: ProfileEntry
|
||||
attributes:
|
||||
- notnull
|
||||
throws:
|
||||
- name: 'IllegalArgumentException'
|
||||
description: 'When the provided ProfileEntry is <code>null</code>'
|
||||
description: 'When the provided ProfileEntry is `null`'
|
||||
type:
|
||||
name: 'void'
|
||||
- name: 'isCancelled'
|
||||
|
||||
+3
-1
@@ -1,11 +1,13 @@
|
||||
---
|
||||
template: 'api-doc.html'
|
||||
api: true
|
||||
|
||||
constructors:
|
||||
- name: 'InvalidPlaceholderProviderException'
|
||||
description: Basic Constructor to create an instance of this Exception.
|
||||
parameters:
|
||||
- name: msg
|
||||
type: String
|
||||
description: The reason of the exception.
|
||||
---
|
||||
|
||||
# <api__class></api__class> InvalidPlaceholderProviderException
|
||||
|
||||
+15
-17
@@ -1,29 +1,27 @@
|
||||
---
|
||||
template: api-doc.html
|
||||
api: true
|
||||
|
||||
methods:
|
||||
- name: 'getName'
|
||||
description: |
|
||||
Returns the name of the player.<br>
|
||||
Note that on Spigot, Paper and other forks can the name differ from the one cached by AdvancedServerList, if the plugin was able to retrieve an OfflinePlayer instance from the server. On BungeeCord and Velocity will the returned String always be the name from the cache.<br>
|
||||
<br>
|
||||
Name may be whatever has been defined in AdvancedServerList's config.yml, should the player not be cached yet by the plugin.
|
||||
Returns the name of the player.
|
||||
On BungeeCord and Velocity is the returned value always what AdvancedServerList has cached, while on paper the value may differ, should the plugin be able to obtain a OfflinePlayer instance from the Server.
|
||||
|
||||
Name may also be whatever has been defined in AdvancedServerList's `unknownPlayer -> name` config.yml Option.
|
||||
returns: 'String representing the player''s name'
|
||||
type:
|
||||
name: String
|
||||
type: object
|
||||
- name: 'getProtocol'
|
||||
description: |
|
||||
Returns the protocol ID the player is using.<br>
|
||||
The protocol ID is an integer used by Minecraft to determine what version a server or client is running.
|
||||
description: Returns the protocol ID the player is using. The protocol ID is a unique positive integer that is used in Minecraft to determine the Version used by the Client and Server.
|
||||
returns: 'Integer representing the protocol version of this player.'
|
||||
type:
|
||||
name: int
|
||||
- name: 'getUUID'
|
||||
description: |
|
||||
Returns the unique ID associated with this player.<br>
|
||||
<br>
|
||||
UUID may be whatever has been defined in AdvancedServerList's config.yml, should the player not be cached yet by AdvancedServerList.
|
||||
Returns the unique ID associated with this player.
|
||||
|
||||
UUID may also be whatever has been defined in AdvancedServerList's `unknownPlayer -> uuid` config.yml Option.
|
||||
returns: 'UUID of the player'
|
||||
type:
|
||||
name: UUID
|
||||
@@ -32,11 +30,11 @@ methods:
|
||||
|
||||
# <api__interface></api__interface> GenericPlayer
|
||||
|
||||
A basic interface used to store generic player data to use in placeholder resolving.
|
||||
The different variants of AdvancedServerList implement this into their own class and may provide additional data not available through this interface.
|
||||
Interface used by the API to share common information across all platforms.
|
||||
|
||||
The following values will always be present, no matter the platform this interface is used on:
|
||||
Platform-specific interfaces extending this interface are available and may offer additional features in addition to what this interface is offering.
|
||||
This interface always offers data for the following:
|
||||
|
||||
- [Name of the Player](#getname())
|
||||
- [Protocol version of the Player](#getprotocol())
|
||||
- [UUID of the Player](#getuuid())
|
||||
- [Name of the Player](#getname)
|
||||
- [Protocol version of the Player](#getprotocol)
|
||||
- [UUID of the Player](#getuuid)
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
template: api-doc.html
|
||||
api: true
|
||||
|
||||
methods:
|
||||
- name: 'getPlayersOnline'
|
||||
@@ -24,4 +24,11 @@ methods:
|
||||
|
||||
# <api__interface></api__interface> GenericServer
|
||||
|
||||
Simple class used to wrap around some generic server data such as online player count, number of total players that can join and the host (IP/Domain) that got pinged by the player.
|
||||
Interface used to share common information across all platforms.
|
||||
|
||||
Platform-specific interfaces extending this interface are available and may offer additional features in addition to what this interface is offering.
|
||||
This interface always offers data for the following:
|
||||
|
||||
- [Players currently online on the Proxy/Server](#getplayersonline)
|
||||
- [Max Players allowed to join the Proxy/Server](#getplayersmax)
|
||||
- [IP/Domain pinged by the Player](#gethost)
|
||||
Loaded 100 of 202 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user