mirror of
https://codeberg.org/Andre601/AdvancedServerList.git
synced 2026-11-05 09:00:15 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5729b9d861 | ||
|
|
3e94cf1377 | ||
|
|
c4e5d00122 | ||
|
|
5d819d1dbc | ||
|
|
8e7873fca2 | ||
|
|
b88c54d3ca | ||
|
|
713e49a11c | ||
|
|
eedbc88457 | ||
|
|
a0d0b71a2a | ||
|
|
639240bfb5 | ||
|
|
b1cd0d1058 | ||
|
|
c3b75ec1b7 | ||
|
|
4e8cb94908 | ||
|
|
c95c900a0e | ||
|
|
73f826b553 | ||
|
|
fea92bab5f | ||
|
|
1e9c042795 | ||
|
|
1f4672a6a2 | ||
|
|
8a60206af6 | ||
|
|
51b0123e5d | ||
|
|
c2c6dde058 | ||
|
|
53a1c55634 | ||
|
|
c5588f6981 | ||
|
|
bac9c7e0f4 | ||
|
|
f6c3f0f590 | ||
|
|
31f66caef7 | ||
|
|
f4c9fae031 | ||
|
|
9e212fa2ea | ||
|
|
3a25d6185c | ||
|
|
5cf4887ae8 | ||
|
|
e9bb64affa | ||
|
|
df34843b7f | ||
|
|
ee5b7d022a | ||
|
|
94fbcaa5f4 | ||
|
|
850becd183 | ||
|
|
2a488248af | ||
|
|
2e78b59c54 | ||
|
|
4919caefec | ||
|
|
158cac6bbc | ||
|
|
babbadb495 | ||
|
|
772e25f9f9 | ||
|
|
f649c18e51 | ||
|
|
6cdadae7b4 | ||
|
|
c36cb3b9ae | ||
|
|
1776de1ec0 | ||
|
|
0ae7f0990f | ||
|
|
53c4d738af | ||
|
|
97d6b9cf2f | ||
|
|
cd390ad95e | ||
|
|
5a204b2650 | ||
|
|
ffce2c7b8d | ||
|
|
1fb5eabcad | ||
|
|
7c53ea19e0 | ||
|
|
a614d1f2aa | ||
|
|
417303e804 | ||
|
|
9956b02736 | ||
|
|
43b3ddf81c | ||
|
|
f0a0c43351 | ||
|
|
734c124a65 | ||
|
|
741324d1c7 | ||
|
|
b28ef832c8 | ||
|
|
2b1a5aa45b | ||
|
|
c9581fe79c | ||
|
|
d385b68d2c | ||
|
|
dd82892d39 | ||
|
|
32239db674 | ||
|
|
16ece42116 | ||
|
|
137cf6e8da | ||
|
|
0ba3047acc | ||
|
|
de7d0ed3ca | ||
|
|
7c6bb28d39 | ||
|
|
0ce9ec0239 | ||
|
|
1aa196c3d5 | ||
|
|
5c7366198e | ||
|
|
cf7fe7236e | ||
|
|
e69c94da06 | ||
|
|
7cd3835785 | ||
|
|
8c36d27360 | ||
|
|
5329ed1b88 | ||
|
|
d66494f4dd | ||
|
|
d03ebf5fb1 | ||
|
|
c7ae2c130f | ||
|
|
017a1893c4 | ||
|
|
7a3af4cb26 | ||
|
|
0d05f2fe1d | ||
|
|
0acb097439 | ||
|
|
8a67fbead8 | ||
|
|
8f3252d66a | ||
|
|
7668a680c4 | ||
|
|
944e5a7262 | ||
|
|
d7cefb77ce | ||
|
|
e7a44711c0 | ||
|
|
96750ec5fd | ||
|
|
16500f29e4 | ||
|
|
25e433d0ed | ||
|
|
21430e9a4e | ||
|
|
de5455ab39 | ||
|
|
a52ca1d8be | ||
|
|
34d8048dd4 | ||
|
|
3b1e676888 | ||
|
|
9332a3e342 | ||
|
|
eacf227650 | ||
|
|
56131f4478 | ||
|
|
b362c34141 | ||
|
|
41718611e7 | ||
|
|
a92d6716e4 | ||
|
|
768509100d | ||
|
|
a64ad0cfa0 | ||
|
|
2abde6ef63 | ||
|
|
da2cf4d501 | ||
|
|
2a00634ce2 | ||
|
|
58108a8b19 | ||
|
|
6e7260d8f2 | ||
|
|
d75f43a4bd | ||
|
|
442c1539e9 | ||
|
|
a6188905dc | ||
|
|
81e0778825 | ||
|
|
afdcd11f7c | ||
|
|
4ca00cd8ad | ||
|
|
3b2d6b98b2 | ||
|
|
348d1fcacf | ||
|
|
4451d2382b | ||
|
|
3d410631ef | ||
|
|
e0ba710aed | ||
|
|
966d817f29 | ||
|
|
838416b1c5 | ||
|
|
23ee4a08ad | ||
|
|
baba7c7076 | ||
|
|
7e7228dd77 | ||
|
|
103bf4f1db | ||
|
|
0c7c8e56db | ||
|
|
cb3161a118 | ||
|
|
326fe7098a | ||
|
|
d79c54f7c9 | ||
|
|
1468febf72 | ||
|
|
afbcad6c3e | ||
|
|
79aefaa6a5 | ||
|
|
d6ac483a0e | ||
|
|
35df0d84ca | ||
|
|
45229bd6d2 | ||
|
|
7bfa88383d | ||
|
|
efaae58562 | ||
|
|
7dfdbb621d | ||
|
|
d2cd554fb1 | ||
|
|
69168efb01 | ||
|
|
55f94a8c27 | ||
|
|
e813bdaba4 | ||
|
|
b3f56aa398 | ||
|
|
87f0573162 | ||
|
|
e49fe391d0 | ||
|
|
d077efdc12 | ||
|
|
90fdaf628c | ||
|
|
9651011b19 | ||
|
|
9bacc04a68 | ||
|
|
515081e395 | ||
|
|
95fec87c12 | ||
|
|
778a415b4f | ||
|
|
db66945d99 | ||
|
|
cf4245842b | ||
|
|
d90084196b | ||
|
|
62b13a3cdf | ||
|
|
be6e5f8bfb | ||
|
|
aaf1439da8 | ||
|
|
b304252140 | ||
|
|
dda321f07b | ||
|
|
f487fd9cc1 | ||
|
|
5681e7a790 | ||
|
|
c3aa8bf536 | ||
|
|
1ce27b2244 | ||
|
|
74bce4c220 | ||
|
|
6605538b0f | ||
|
|
4aa9e946fd | ||
|
|
42ec17997e | ||
|
|
d2587e67f4 | ||
|
|
3f8f0e0ff7 | ||
|
|
b0dbbbfe82 | ||
|
|
6a32ea7826 | ||
|
|
a6bcc7036c | ||
|
|
639b32db23 | ||
|
|
53672243d3 | ||
|
|
3b7e177e4f | ||
|
|
6b43ef4dc3 | ||
|
|
665990aae9 | ||
|
|
70723ce35e | ||
|
|
b2d689306b | ||
|
|
6ae3f0aea6 | ||
|
|
9445e12f8d | ||
|
|
94da2bbd2d | ||
|
|
b5d0246bce | ||
|
|
48b1e53d13 | ||
|
|
5e7a59474d | ||
|
|
bfbc547712 | ||
|
|
2f8273d6a3 | ||
|
|
1f036727d8 | ||
|
|
bc07dadf03 | ||
|
|
ac0c754050 | ||
|
|
b2d664b5b9 | ||
|
|
58e9e55dc6 | ||
|
|
9b8e459b5c | ||
|
|
598343ebc7 | ||
|
|
c1fd983dab | ||
|
|
d05faf30dc | ||
|
|
79a96f30ca | ||
|
|
54ddf4bb4d | ||
|
|
f8b85ce9ed | ||
|
|
2f5c1db3c1 | ||
|
|
de7570ac6e | ||
|
|
41c7db5f27 | ||
|
|
ef0ef4e373 | ||
|
|
e9ab05618e | ||
|
|
a28e62f53c | ||
|
|
e82df2d1f7 | ||
|
|
16a2b05b2b | ||
|
|
5df3d6dd48 | ||
|
|
91a6149dac | ||
|
|
3f224504a1 | ||
|
|
437e155a46 | ||
|
|
8275069315 | ||
|
|
bd564a8638 | ||
|
|
591279f70b | ||
|
|
80e84aa41b | ||
|
|
9024c4dc71 | ||
|
|
bb0f389016 | ||
|
|
f8f8a45b1a | ||
|
|
0bae24bb66 | ||
|
|
2158caebe6 | ||
|
|
f9ea253da3 | ||
|
|
7c53ddf4f4 | ||
|
|
8bcc88d620 | ||
|
|
78b687587d | ||
|
|
168fe364b0 | ||
|
|
cd3f54dba6 | ||
|
|
79f15b2467 | ||
|
|
ade8ad5a33 | ||
|
|
6e343f1516 | ||
|
|
fee436fda6 | ||
|
|
27d4f5acd0 | ||
|
|
e18c70d446 | ||
|
|
0ca3ec62a9 | ||
|
|
461ae5d0aa | ||
|
|
5696a9417b | ||
|
|
0705101cd6 | ||
|
|
69ad3554cf | ||
|
|
7f1de13dcc | ||
|
|
c3e16eec31 | ||
|
|
207970a90b | ||
|
|
2628dbab56 | ||
|
|
3da9f8b400 | ||
|
|
ffd0a8c364 | ||
|
|
d9a7b244f9 | ||
|
|
f1fb5ef6ea | ||
|
|
d3406aad1c | ||
|
|
650bcc3235 | ||
|
|
0cd91c63bb | ||
|
|
e95a4634ec | ||
|
|
d0293ae15e | ||
|
|
f5c6185b96 | ||
|
|
61981db3cc | ||
|
|
52804adfdf | ||
|
|
4b185be8a4 | ||
|
|
46ac4ef5c1 | ||
|
|
00e2a8ebee | ||
|
|
c523da7477 | ||
|
|
01e586fa5e | ||
|
|
9322819538 | ||
|
|
7ef4a9a61b | ||
|
|
af2da61500 | ||
|
|
23d0626250 | ||
|
|
74dfdb3022 | ||
|
|
4a589619bd | ||
|
|
9bdd519246 | ||
|
|
cf4e08fe3e | ||
|
|
70bbb15c60 | ||
|
|
ee8e00f3a2 | ||
|
|
0280668f9b | ||
|
|
e959f478a0 | ||
|
|
f024b2c763 | ||
|
|
c0665b450e | ||
|
|
e50ed552b9 | ||
|
|
1601e41e9c | ||
|
|
f8b80f9e14 | ||
|
|
22bfe2be00 | ||
|
|
7e9c6b909c | ||
|
|
93ecdd736f | ||
|
|
abfbaf4498 | ||
|
|
ce3b80b05c | ||
|
|
9e3a77ad7d | ||
|
|
cdc2115cb3 | ||
|
|
5173f61c6c | ||
|
|
8b12cb2402 | ||
|
|
3ab48396af | ||
|
|
00b34c8b47 | ||
|
|
b06274638e | ||
|
|
cd3b2fe0b4 | ||
|
|
51d8ae6c42 | ||
|
|
1545be4709 | ||
|
|
7693e72728 | ||
|
|
b879110379 | ||
|
|
80900089bb | ||
|
|
df776f4e1c | ||
|
|
72afff8315 | ||
|
|
307384d1e1 | ||
|
|
84f31fad53 | ||
|
|
fe0774303a | ||
|
|
563173679b | ||
|
|
6e96e06109 | ||
|
|
dae25fd00a | ||
|
|
d2ca0cfae5 | ||
|
|
afaae368ac | ||
|
|
522ce7f932 | ||
|
|
3f3d460f2f | ||
|
|
3a03899688 | ||
|
|
e6d4264525 | ||
|
|
61466074fd | ||
|
|
4516157ddf | ||
|
|
82c65bf13c | ||
|
|
3c6e73671d | ||
|
|
c91a2c0d4e | ||
|
|
66b10656c3 | ||
|
|
b82a0ff0d9 | ||
|
|
2e02af5a67 | ||
|
|
5626b38390 | ||
|
|
415456447c | ||
|
|
c555a3c648 | ||
|
|
9ec0c1c4ce | ||
|
|
d3eb43db39 | ||
|
|
b4b4f6a9fc | ||
|
|
3217eee02b | ||
|
|
ea68fca70b | ||
|
|
a8ceb88b67 | ||
|
|
19d6ce28c4 | ||
|
|
05e61794a1 | ||
|
|
46e062eb8f | ||
|
|
2196456dd4 | ||
|
|
bfd1e2e2ad | ||
|
|
21af6df510 | ||
|
|
713062d1b4 | ||
|
|
673a134ab1 | ||
|
|
c777772a90 | ||
|
|
a43a9f8f19 | ||
|
|
d4a416f85e | ||
|
|
bc26ceae55 | ||
|
|
3d19a6688c | ||
|
|
03a8258ddc | ||
|
|
6df5015b0d | ||
|
|
f1f8aaaad2 | ||
|
|
151f5494fc | ||
|
|
f997fa0a89 | ||
|
|
2f1549eb8a | ||
|
|
77dba75c17 | ||
|
|
dfa72cb852 | ||
|
|
dc0cf6ebfc | ||
|
|
2c520e53c0 | ||
|
|
356cca9ba0 | ||
|
|
4e7b17a601 | ||
|
|
033f4bd6ab | ||
|
|
13edca3c4c | ||
|
|
a626dc5b28 | ||
|
|
7a60024bd0 | ||
|
|
cedb1e5748 | ||
|
|
8b0fb99bc5 | ||
|
|
31202a930a | ||
|
|
a105e3186b | ||
|
|
e2df79c428 | ||
|
|
01ecce170c | ||
|
|
3bb171cecf | ||
|
|
f54bbe26e1 | ||
|
|
ddede7abef | ||
|
|
2c6467f176 | ||
|
|
8b92f8fc4d | ||
|
|
7bf6d8de0f | ||
|
|
844a88d381 | ||
|
|
fcea4e90e3 | ||
|
|
6a97debdf7 | ||
|
|
07fa6cf0ec | ||
|
|
367f0f3501 | ||
|
|
16dcaef825 | ||
|
|
f780372fda | ||
|
|
a8f8c0d770 | ||
|
|
70e06afaf0 | ||
|
|
13e087eddd | ||
|
|
31af916f04 | ||
|
|
0c36667116 | ||
|
|
3417d7c0be | ||
|
|
96fa871204 | ||
|
|
aaacd9944f | ||
|
|
af44324cf1 | ||
|
|
77b6897bd4 | ||
|
|
9d963199ae | ||
|
|
580b87ebb5 | ||
|
|
efd5762162 | ||
|
|
62261ae6ed | ||
|
|
e52bfdf60b | ||
|
|
0152ba3e1a | ||
|
|
090787e292 | ||
|
|
25d14eeaae | ||
|
|
386d490596 | ||
|
|
207ce12452 | ||
|
|
b87703c36c | ||
|
|
41677d0635 | ||
|
|
dfdb2aa767 | ||
|
|
d14c5f80e8 | ||
|
|
bd9e376d08 | ||
|
|
578242078b | ||
|
|
f84842729d | ||
|
|
1daeecbbcf | ||
|
|
5dcab6c37c | ||
|
|
a146a10d53 | ||
|
|
75db0d5e96 | ||
|
|
644dd401d4 | ||
|
|
32d85c6be1 | ||
|
|
1b47af6714 | ||
|
|
4544696a54 | ||
|
|
a84848daee | ||
|
|
3f8dd90e28 | ||
|
|
f633741988 | ||
|
|
7e9ea9f470 | ||
|
|
c2c00dc47f | ||
|
|
027503efab | ||
|
|
7dd78cbbfc | ||
|
|
b27b8e78bd | ||
|
|
ad5b07feda | ||
|
|
d79784d1c0 | ||
|
|
ef84e85426 | ||
|
|
35782809ac | ||
|
|
3e3c4903e4 | ||
|
|
79b45fb29d | ||
|
|
46f40294e2 | ||
|
|
ca5ab5d221 | ||
|
|
b2e5d7ae28 | ||
|
|
649e9f07ef | ||
|
|
eaa86aa93b | ||
|
|
8a90619810 | ||
|
|
351db260ce | ||
|
|
bcc9d88e8c | ||
|
|
9c849157d3 | ||
|
|
f171c951e7 | ||
|
|
cae808e3fc | ||
|
|
ae8e6304f9 | ||
|
|
c975ba79b3 | ||
|
|
83fb13e1f2 | ||
|
|
586351e277 | ||
|
|
098b6be0f4 | ||
|
|
1c881d2832 | ||
|
|
e6f3d122c7 | ||
|
|
759474b344 | ||
|
|
6aef4c2de9 | ||
|
|
64d7c81c58 | ||
|
|
a070708323 | ||
|
|
e9ba86508c | ||
|
|
c1e3638534 | ||
|
|
f5260f3be1 | ||
|
|
51372aa8f4 | ||
|
|
025c9f4423 | ||
|
|
99c34dd343 | ||
|
|
cd91b799d6 | ||
|
|
5b7eb014a3 | ||
|
|
ad32952b97 | ||
|
|
4a1e552d5d | ||
|
|
ea1394dce9 | ||
|
|
317fa631e9 | ||
|
|
5dd26f6ab8 | ||
|
|
ba82df45ef | ||
|
|
d76339188d | ||
|
|
a9fb8f3ed6 | ||
|
|
18291a6865 | ||
|
|
83a1808ebc | ||
|
|
197967dea9 | ||
|
|
b25b65faba | ||
|
|
fafcd3ae18 | ||
|
|
daae88074f | ||
|
|
fbe019214d | ||
|
|
3e380342a7 | ||
|
|
fd2dbd2edb | ||
|
|
e969208ae9 | ||
|
|
77aa404e06 | ||
|
|
a6c1840858 | ||
|
|
762bd1d0fd | ||
|
|
02ce580433 | ||
|
|
c7b7feb476 | ||
|
|
0e145def83 | ||
|
|
03e32feb1d | ||
|
|
dbab282901 | ||
|
|
bd6b599267 | ||
|
|
1a63430c1e | ||
|
|
661f9e6ad4 | ||
|
|
6bd8d40ff4 | ||
|
|
16b74433ff | ||
|
|
1aaafe474f | ||
|
|
26a7697b0f | ||
|
|
ec6b653988 | ||
|
|
854eb67c54 | ||
|
|
00511f6e97 | ||
|
|
16eebb23a5 | ||
|
|
faae641c32 | ||
|
|
4fa542a242 | ||
|
|
2dc1f7ccc1 | ||
|
|
dd7b1a7392 | ||
|
|
a90d241cd2 | ||
|
|
452ac00ee7 | ||
|
|
f139270f0e | ||
|
|
b212b2af0f | ||
|
|
0d506379cf | ||
|
|
995793e66d | ||
|
|
f9ebc60d64 | ||
|
|
1d531a32e3 | ||
|
|
36340d9b9d | ||
|
|
d023e22934 | ||
|
|
260a0b4835 | ||
|
|
3f18aaaf5b | ||
|
|
3f0e7897d4 | ||
|
|
4bd35c810b | ||
|
|
b081be8f53 | ||
|
|
eb474d061b | ||
|
|
edbc893c65 | ||
|
|
d03dce9b29 | ||
|
|
db06de243f |
No files matched your search
@@ -10,34 +10,28 @@ on:
|
||||
- ".forgejo/workflows/deploy_wiki.yml"
|
||||
- "docs/**"
|
||||
- "domains"
|
||||
- "mkdocs.yml"
|
||||
- "properdocs.yml"
|
||||
- "requirements.txt"
|
||||
|
||||
jobs:
|
||||
deployWiki:
|
||||
runs-on: codeberg-tiny
|
||||
runs-on: codeberg-small
|
||||
steps:
|
||||
- name: "Checkout Source Repository"
|
||||
uses: https://code.forgejo.org/actions/checkout@v4
|
||||
- name: "Checkout Repository"
|
||||
uses: https://code.forgejo.org/actions/checkout@v7
|
||||
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@v5
|
||||
uses: https://code.forgejo.org/actions/setup-python@v7
|
||||
with:
|
||||
python-version: 3.x
|
||||
- name: "Install Dependencies"
|
||||
run: "pip install -r requirements.txt"
|
||||
- name: "Build Website"
|
||||
run: "mkdocs 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
|
||||
run: "properdocs build"
|
||||
- name: "Deploy Website"
|
||||
uses: https://code.forgejo.org/actions/git-pages@v2
|
||||
with:
|
||||
site: https://asl.andre601.ch
|
||||
token: ${{ secrets.FORGEJO_TOKEN }}
|
||||
source: site/
|
||||
@@ -9,7 +9,7 @@ on:
|
||||
paths:
|
||||
- ".forgejo/workflows/pr_preview.yml"
|
||||
- "docs/**"
|
||||
- "mkdocs.yml"
|
||||
- "properdocs.yml"
|
||||
- "preview_domains"
|
||||
- "requirements.txt"
|
||||
workflow_dispatch:
|
||||
@@ -20,7 +20,7 @@ env:
|
||||
|
||||
jobs:
|
||||
createPreview:
|
||||
runs-on: codeberg-tiny
|
||||
runs-on: codeberg-small
|
||||
steps:
|
||||
#
|
||||
# Finding and updating comment in Pull request.
|
||||
@@ -28,13 +28,13 @@ jobs:
|
||||
#
|
||||
- name: "Find Comment"
|
||||
id: find-comment
|
||||
uses: https://github.com/peter-evans/find-comment@v3
|
||||
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@v4
|
||||
uses: https://github.com/peter-evans/create-or-update-comment@v5
|
||||
with:
|
||||
body: |-
|
||||
## Pull request Preview
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
# Build website and push.
|
||||
#
|
||||
- name: "Checkout Source Repository"
|
||||
uses: https://code.forgejo.org/actions/checkout@v4
|
||||
uses: https://code.forgejo.org/actions/checkout@v7
|
||||
with:
|
||||
token: ${{ secrets.BOT_TOKEN }}
|
||||
fetch-depth: 0
|
||||
@@ -60,13 +60,13 @@ jobs:
|
||||
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@v5
|
||||
uses: https://code.forgejo.org/actions/setup-python@v7
|
||||
with:
|
||||
python-version: 3.x
|
||||
- name: "Install Dependencies"
|
||||
run: "pip install -r requirements.txt"
|
||||
- name: "Build Website"
|
||||
run: "mkdocs build"
|
||||
run: "properdocs build"
|
||||
- name: "Commit and Push"
|
||||
run: |
|
||||
git config --global user.email "andre_601-bot@noreply.codeberg.org"
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
# Update comment on success or failure.
|
||||
#
|
||||
- name: "Comment Success"
|
||||
uses: https://github.com/peter-evans/create-or-update-comment@v4
|
||||
uses: https://github.com/peter-evans/create-or-update-comment@v5
|
||||
with:
|
||||
body: |-
|
||||
## Pull request Preview
|
||||
@@ -99,7 +99,7 @@ jobs:
|
||||
edit-mode: replace
|
||||
- name: "Comment Failure"
|
||||
if: ${{ failure() }}
|
||||
uses: https://github.com/peter-evans/create-or-update-comment@v4
|
||||
uses: https://github.com/peter-evans/create-or-update-comment@v5
|
||||
with:
|
||||
body: |-
|
||||
## Pull request Preview
|
||||
@@ -109,7 +109,7 @@ jobs:
|
||||
| Name | Link |
|
||||
|---------|-------------------------------------------|
|
||||
| Commit | ${{ github.event.pull_request.head.sha }} |
|
||||
| Logs | ${{ RUN_URL }} |
|
||||
| Logs | ${{ env.RUN_URL }} |
|
||||
comment-id: "${{ steps.comment.outputs.comment-id }}"
|
||||
issue-number: "${{ github.event.pull_request.number }}"
|
||||
token: "${{ secrets.BOT_TOKEN }}"
|
||||
|
||||
@@ -16,13 +16,14 @@ body:
|
||||
label: Platform
|
||||
description: On what platform do you use the plugin?
|
||||
options:
|
||||
- BungeeCord
|
||||
- Other (Please specify)
|
||||
- Paper
|
||||
- Purpur
|
||||
- Spigot
|
||||
- Folia
|
||||
- Velocity
|
||||
- Waterfall
|
||||
- Other (Please specify)
|
||||
- Spigot (DISCONTINUED)
|
||||
- BungeeCord (DISCONTINUED)
|
||||
- Waterfall (DISCONTINUED)
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
@@ -32,6 +33,35 @@ body:
|
||||
Please provide the name of the platform (Proxy/Server) you use, if you selected `Other (Please specify)` in the above section.
|
||||
Leave empty otherwise.
|
||||
placeholder: SomeProxy
|
||||
- type: input
|
||||
attributes:
|
||||
label: Server/Proxy version
|
||||
description: |-
|
||||
Provide the Version of your Server/Proxy.
|
||||
For Paper and its forks, run `/version` and for Velocity run `/velocity info`
|
||||
placeholder: This server is running Paper version ...
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Plugin version
|
||||
description: |-
|
||||
Provide the Version of the Plugin.
|
||||
On Paper and its forks, run `/version AdvancedServerList` and on Velocity, run `/velocity plugins` and find the AdvancedServerList entry.
|
||||
placeholder: 5.9.0
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Logs
|
||||
description: |-
|
||||
Provide Logs from your Server/Proxy, uploaded to [mclo.gs](https://mclo.gs).
|
||||
|
||||
**DO NOT SKIP THIS!**
|
||||
Skipping this by writing "Not needed", "private", etc. will get your issue closed without warning!
|
||||
placeholder: 'https://mclo.gs/...'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What happened?
|
||||
@@ -49,7 +79,7 @@ body:
|
||||
placeholder: |-
|
||||
1. Do this
|
||||
2. ...
|
||||
3. Profit!
|
||||
3. Prof-... Bug!
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
when:
|
||||
- event: pull_request
|
||||
path:
|
||||
include: [".woodpecker/pr_preview.yml", "docs/**", "mkdocs.yml", "requirements.txt"]
|
||||
|
||||
variables:
|
||||
setup_git: &setup_git
|
||||
- apk add git
|
||||
- git config --global user.email "andre_601-bot@noreply.codeberg.org"
|
||||
- git config --global user.name "PR Docs Preview Builder"
|
||||
|
||||
steps:
|
||||
- name: "Clone Preview Repository"
|
||||
image: alpine:3.20.3
|
||||
commands:
|
||||
- chmod -R a+w .
|
||||
- <<: *setup_git
|
||||
- git clone https://codeberg.org/Andre601/preview.git site
|
||||
- chmod -R a+w site
|
||||
- cd site
|
||||
- git switch ${CI_REPO_NAME,,}/${CI_COMMIT_PULL_REQUEST} 2>/dev/null || git switch -c ${CI_REPO_NAME,,}/${CI_COMMIT_PULL_REQUEST}
|
||||
- name: "Build Docs"
|
||||
image: woodpeckerci/plugin-mkdocs:latest
|
||||
settings:
|
||||
pip_install_file: requirements.txt
|
||||
- name: "Commit and Push"
|
||||
image: alpine:3.20.3
|
||||
secrets:
|
||||
- bot_token
|
||||
commands:
|
||||
- <<: *setup_git
|
||||
- mv preview-domains site/.domains
|
||||
- cd site
|
||||
- git remote add upstream https://$BOT_TOKEN@codeberg.org/Andre601/preview.git
|
||||
- git add --all
|
||||
- git commit -m "Update Preview for ${CI_REPO_NAME}/${CI_COMMIT_PULL_REQUEST} ($( env TZ=Europe/Zurich date +"%d.%m.%Y %Z" )) [SKIP CI]"
|
||||
- git push --set-upstream upstream ${CI_REPO_NAME,,}/${CI_COMMIT_PULL_REQUEST} -f
|
||||
- name: "Comment on Success"
|
||||
image: mcs94/gitea-comment
|
||||
when:
|
||||
- status: ["success"]
|
||||
settings:
|
||||
gitea_address: https://codeberg.org
|
||||
gitea_token:
|
||||
from_secret: BOT_TOKEN
|
||||
comment: |-
|
||||
Pull request Preview Ready!
|
||||
|
||||
| Name | Value |
|
||||
|--------|--------------------------------------------------------------------------|
|
||||
| Commit | ${CI_COMMIT_SHA} |
|
||||
| Link | https://preview.andre601.ch/@${CI_REPO_NAME,,}~${CI_COMMIT_PULL_REQUEST} |
|
||||
- name: "Comment on Failure"
|
||||
image: mcs94/gitea-comment
|
||||
when:
|
||||
- status: ["failure"]
|
||||
settings:
|
||||
gitea_address: https://codeberg.org
|
||||
gitea_token:
|
||||
from_secret: BOT_TOKEN
|
||||
comment: |-
|
||||
Pull request Preview **failed**!
|
||||
|
||||
Please check the CI logs for details: ${CI_PIPELINE_URL}
|
||||
@@ -4,7 +4,7 @@ when:
|
||||
|
||||
steps:
|
||||
- name: "Build Jar and Upload"
|
||||
image: maven:3.9.9-eclipse-temurin-21
|
||||
image: maven:3.10.0-eclipse-temurin-25
|
||||
environment:
|
||||
CODEBERG_API_TOKEN:
|
||||
from_secret: cb_token
|
||||
@@ -19,16 +19,16 @@ steps:
|
||||
- cd version-uploader/target
|
||||
- mv VersionUploader.jar ../../
|
||||
- cd ../../
|
||||
- java -jar VersionUploader.jar --all
|
||||
- java -jar VersionUploader.jar upload all
|
||||
- name: "Upload Jars to Codeberg"
|
||||
image: woodpeckerci/plugin-gitea-release
|
||||
settings:
|
||||
base_url: https://codeberg.org
|
||||
files:
|
||||
- "bungeecord/target/AdvancedServerList-*.jar"
|
||||
- "paper/target/AdvancedServerList-*.jar"
|
||||
- "velocity/target/AdvancedServerList-*.jar"
|
||||
- "addons/banplugins/target/AdvancedServerList-*.jar"
|
||||
- "addons/luckperms/target/AdvancedServerList-*.jar"
|
||||
api_key:
|
||||
from_secret: cb_token
|
||||
- name: "Create Post on Mastodon"
|
||||
@@ -45,4 +45,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: en-US
|
||||
text: |-
|
||||
#AdvancedServerList was updated to ${CI_COMMIT_TAG} and can be downloaded from #Modrinth, #Hangar and #Codeberg!
|
||||
|
||||
Modrinth - https://modrinth.com/plugin/advancedserverlist
|
||||
Hangar - https://hangar.papermc.io/Andre_601/AdvancedServerList
|
||||
Codeberg - https://codeberg.org/Andre601/AdvancedServerList
|
||||
@@ -1,36 +0,0 @@
|
||||
when:
|
||||
- event: push
|
||||
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||
path:
|
||||
include: [".woodpecker/publish_docs.yml", "docs/**", "mkdocs.yml", "requirements.txt"]
|
||||
|
||||
variables:
|
||||
setup_git: &setup_git
|
||||
- apk add git
|
||||
- git config --global user.email "andre_601-bot@noreply.codeberg.org"
|
||||
- git config --global user.name "ASL Docs Builder"
|
||||
|
||||
steps:
|
||||
- name: "Clone target Repository"
|
||||
image: alpine:3.20.3
|
||||
commands:
|
||||
- chmod -R a+w .
|
||||
- <<: *setup_git
|
||||
- git clone -b pages https://codeberg.org/Andre601/asl-docs.git site
|
||||
- chmod -R a+w site
|
||||
- name: "Build Docs"
|
||||
image: woodpeckerci/plugin-mkdocs:latest
|
||||
settings:
|
||||
pip_install_file: requirements.txt
|
||||
- name: "Commit and Push"
|
||||
image: alpine:3.20.3
|
||||
secrets:
|
||||
- bot_token
|
||||
commands:
|
||||
- <<: *setup_git
|
||||
- mv domains site/.domains
|
||||
- cd site
|
||||
- git remote set-url origin https://$BOT_TOKEN@codeberg.org/Andre601/asl-docs.git
|
||||
- git add --all
|
||||
- git commit -m "Update AdvancedServerList Docs ($( env TZ=Europe/Zurich date +"%d.%m.%Y %Z" )) [SKIP CI]"
|
||||
- git push
|
||||
@@ -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 |
|
||||
@@ -92,6 +92,12 @@ English Video by KasaiSora
|
||||
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
-5
@@ -19,6 +19,8 @@ To ensure proper safety and support are only certain versions and Platforms of A
|
||||
|
||||
| Server/Proxy Version* | Supported |
|
||||
|-----------------------|-----------|
|
||||
| 26.2 | Yes |
|
||||
| 26.1.x | Yes |
|
||||
| 1.21.x | Yes |
|
||||
| 1.20.x | Yes |
|
||||
| 1.19.x | No |
|
||||
@@ -53,8 +55,6 @@ To ensure proper safety and support are only certain versions and Platforms of A
|
||||
|
||||
| Proxy | Supported |
|
||||
|------------|-----------|
|
||||
| BungeeCord | Yes** |
|
||||
| Waterfall | Yes** |
|
||||
| Velocity | Yes |
|
||||
|
||||
\*\*Platform version of this plugin is maintenance only, not receiving any bug fixes for major breaking changes anymore.
|
||||
| BungeeCord | No |
|
||||
| Waterfall | No |
|
||||
| Velocity | Yes |
|
||||
@@ -62,30 +62,16 @@
|
||||
|
||||
<dependencies>
|
||||
<!-- Proxy/Server -->
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.20-R0.2</version>
|
||||
<type>jar</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.20-R0.2</version>
|
||||
<type>javadoc</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<version>[26.1.2.build,)</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.velocitypowered</groupId>
|
||||
<artifactId>velocity-api</artifactId>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
<version>4.3.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -99,13 +85,13 @@
|
||||
<dependency>
|
||||
<groupId>space.arim.libertybans</groupId>
|
||||
<artifactId>bans-api</artifactId>
|
||||
<version>1.1.0-SNAPSHOT</version>
|
||||
<version>1.1.4</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>
|
||||
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.banplugins.bungeecord;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.AdvancedServerListAPI;
|
||||
import ch.andre601.advancedserverlist.banplugins.BanPluginsList;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
|
||||
public class BungeeCordBanPluginsAddon extends Plugin{
|
||||
|
||||
@Override
|
||||
public void onEnable(){
|
||||
if(getProxy().getPluginManager().getPlugin("AdvancedServerList") == null){
|
||||
getLogger().warning("AdvancedServerList is not enabled. This plugin requires it to function!");
|
||||
return;
|
||||
}
|
||||
|
||||
int loadedPlaceholders = loadPlaceholderProviders();
|
||||
if(loadedPlaceholders == 0){
|
||||
getLogger().warning("No compatible Ban plugin was found to register placeholders for.");
|
||||
}else{
|
||||
getLogger().info("Loaded " + loadedPlaceholders + " Placeholder Set(s) for AdvancedServerList!");
|
||||
}
|
||||
}
|
||||
|
||||
private int loadPlaceholderProviders(){
|
||||
int loaded = 0;
|
||||
AdvancedServerListAPI api = AdvancedServerListAPI.get();
|
||||
|
||||
for(BanPluginsList entry : BanPluginsList.values()){
|
||||
if(!entry.supportsBungeeCord())
|
||||
continue;
|
||||
|
||||
if(getProxy().getPluginManager().getPlugin(entry.getName()) != null){
|
||||
getLogger().info("Registering Placeholders for " + entry.getName() + "...");
|
||||
api.addPlaceholderProvider(entry.getPlaceholderProvider());
|
||||
getLogger().info("Placeholders successfully registered!");
|
||||
|
||||
loaded++;
|
||||
}
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
name: 'ASLBanPluginsAddon'
|
||||
author: 'Andre_601'
|
||||
version: '${plugin.version}'
|
||||
description: 'AdvancedBan Add-on for AdvancedServerList to add placeholders to use.'
|
||||
|
||||
softDepends:
|
||||
- AdvancedServerList
|
||||
- AdvancedBan
|
||||
- LibertyBans
|
||||
- LiteBans
|
||||
|
||||
main: 'ch.andre601.advancedserverlist.banplugins.bungeecord.BungeeCordBanPluginsAddon'
|
||||
@@ -0,0 +1,112 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ MIT License
|
||||
~
|
||||
~ Copyright (c) 2022-2024 Andre_601
|
||||
~
|
||||
~ Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
~ of this software and associated documentation files (the "Software"), to deal
|
||||
~ in the Software without restriction, including without limitation the rights
|
||||
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
~ copies of the Software, and to permit persons to whom the Software is
|
||||
~ furnished to do so, subject to the following conditions:
|
||||
~
|
||||
~ The above copyright notice and this permission notice shall be included in all
|
||||
~ copies or substantial portions of the Software.
|
||||
~
|
||||
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
~ SOFTWARE.
|
||||
~
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>ch.andre601.advancedserverlist</groupId>
|
||||
<version>parent</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>luckperms</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>${plugin.version}</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>sonatype</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>papermc</id>
|
||||
<url>https://repo.papermc.io/repository/maven-public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<!-- Proxy/Server -->
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.21-R0.4</version>
|
||||
<type>jar</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.21-R0.4</version>
|
||||
<type>javadoc</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>[26.1.2.build,)</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.velocitypowered</groupId>
|
||||
<artifactId>velocity-api</artifactId>
|
||||
<version>4.3.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Plugin APIs -->
|
||||
<dependency>
|
||||
<groupId>ch.andre601.asl-api</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>${api.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.luckperms</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>5.5</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>AdvancedServerList-LuckPerms-Addon-${plugin.version}${project.build.build_id}</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<filtering>true</filtering>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
</project>
|
||||
+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,16 @@
|
||||
name: 'ASLLuckPermsAddon'
|
||||
author: 'Andre_601'
|
||||
version: '${plugin.version}'
|
||||
|
||||
main: 'ch.andre601.advancedserverlist.luckperms.paper.LuckPermsAddon'
|
||||
|
||||
api-version: '1.19'
|
||||
|
||||
dependencies:
|
||||
server:
|
||||
AdvancedServerList:
|
||||
required: false
|
||||
load: BEFORE
|
||||
LuckPerms:
|
||||
required: false
|
||||
load: BEFORE
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"id": "aslluckpermsaddon",
|
||||
"name": "ASLLuckPermsAddon",
|
||||
"version": "${plugin.version}",
|
||||
"description": "LuckPerms Add-on for AdvancedServerList to add placeholders to use.",
|
||||
"authors": [
|
||||
"Andre_601"
|
||||
],
|
||||
"dependencies": [
|
||||
{
|
||||
"id": "advancedserverlist",
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"id": "luckperms",
|
||||
"optional": true
|
||||
}
|
||||
],
|
||||
"main": "ch.andre601.advancedserverlist.luckperms.velocity.LuckPermsAddon"
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ MIT License
|
||||
~
|
||||
~ Copyright (c) 2022-2023 Andre_601
|
||||
~
|
||||
~ Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
~ of this software and associated documentation files (the "Software"), to deal
|
||||
~ in the Software without restriction, including without limitation the rights
|
||||
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
~ copies of the Software, and to permit persons to whom the Software is
|
||||
~ furnished to do so, subject to the following conditions:
|
||||
~
|
||||
~ The above copyright notice and this permission notice shall be included in all
|
||||
~ copies or substantial portions of the Software.
|
||||
~
|
||||
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
~ SOFTWARE.
|
||||
~
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>ch.andre601.advancedserverlist</groupId>
|
||||
<version>parent</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>bungeecord</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>${plugin.version}</version>
|
||||
<description>${plugin.description}</description>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>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>repo-sayandevelopment</id>
|
||||
<url>https://repo.sayandev.org/releases/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>ch.andre601.advancedserverlist</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>${plugin.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.andre601.asl-api</groupId>
|
||||
<artifactId>platform-bungeecord</artifactId>
|
||||
<version>${api.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- BungeeCord API -->
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<type>jar</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.21-R0.1-SNAPSHOT</version>
|
||||
<type>javadoc</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Adventure Library -->
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-api</artifactId>
|
||||
<version>${dependency.adventure-api}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-text-minimessage</artifactId>
|
||||
<version>${dependency.adventure-text-minimessage}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-platform-bungeecord</artifactId>
|
||||
<version>${dependency.adventure-platform-bungeecord}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Metrics -->
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bungeecord</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Configurate -->
|
||||
<dependency>
|
||||
<groupId>org.spongepowered</groupId>
|
||||
<artifactId>configurate-yaml</artifactId>
|
||||
<version>${dependency.configurate-yaml}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Plugins -->
|
||||
<dependency>
|
||||
<groupId>com.github.LeonMangler</groupId>
|
||||
<artifactId>PremiumVanishAPI</artifactId>
|
||||
<version>2.9.0-4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.sayandev</groupId>
|
||||
<artifactId>sayanvanish-api</artifactId>
|
||||
<version>1.6.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.sayandev</groupId>
|
||||
<artifactId>sayanvanish-proxy-bungeecord</artifactId>
|
||||
<version>1.6.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Other -->
|
||||
<dependency>
|
||||
<groupId>com.alessiodp.libby</groupId>
|
||||
<artifactId>libby-bungee</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<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}${project.build.build_id}</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<filtering>true</filtering>
|
||||
<directory>${project.basedir}/src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</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>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
-254
@@ -1,254 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.bungeecord;
|
||||
|
||||
import ch.andre601.advancedserverlist.bungeecord.commands.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.profiles.handlers.FaviconHandler;
|
||||
import com.alessiodp.libby.BungeeLibraryManager;
|
||||
import com.alessiodp.libby.Library;
|
||||
import de.myzelyam.api.vanish.BungeeVanishAPI;
|
||||
import net.kyori.adventure.platform.bungeecord.BungeeAudiences;
|
||||
import net.md_5.bungee.api.Favicon;
|
||||
import net.md_5.bungee.api.ServerPing;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import org.bstats.bungeecord.Metrics;
|
||||
import org.bstats.charts.SimplePie;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.sayandev.sayanvanish.bungeecord.api.SayanVanishBungeeAPI;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class BungeeCordCore extends Plugin implements PluginCore<Favicon>{
|
||||
|
||||
private final PluginLogger logger = new BungeeLogger(this);
|
||||
private final Map<String, ServerPing> fetchedServers = new ConcurrentHashMap<>();
|
||||
|
||||
private boolean successfulLoad = false;
|
||||
|
||||
private AdvancedServerList<Favicon> core;
|
||||
private FaviconHandler<Favicon> faviconHandler = null;
|
||||
private BungeeAudiences audiences = null;
|
||||
private BungeeCommandManagerInterface handler;
|
||||
|
||||
private BungeeLibraryManager libraryManager = null;
|
||||
|
||||
@Override
|
||||
public void onLoad(){
|
||||
// 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){
|
||||
getLogger().info("Library loading completed successfully!");
|
||||
}else{
|
||||
getLogger().warning("Library loading completed with errors!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable(){
|
||||
if(!successfulLoad){
|
||||
logger.warn("There were issues during the plugins Loading-phase! Please see the console for possible reasons.");
|
||||
logger.warn("Aborting further enabling of the plugin to avoid errors.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.audiences = BungeeAudiences.create(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);
|
||||
audiences.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadEvents(){
|
||||
new PlayerJoinEventListener(this);
|
||||
new ProxyPingEventListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMetrics(){
|
||||
new Metrics(this, 15585).addCustomChart(new SimplePie("profiles",
|
||||
() -> String.valueOf(core.getFileHandler().getProfiles().size())
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFaviconHandler(AdvancedServerList<Favicon> core){
|
||||
faviconHandler = new FaviconHandler<>(core);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearFaviconCache(){
|
||||
if(faviconHandler == null)
|
||||
return;
|
||||
|
||||
faviconHandler.cleanCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void downloadLibrary(String groupId, String artifactId, String version){
|
||||
if(libraryManager == null){
|
||||
libraryManager = new BungeeLibraryManager(this);
|
||||
libraryManager.addRepository("https://repo.papermc.io/repository/maven-public");
|
||||
}
|
||||
|
||||
Library lib = Library.builder()
|
||||
.groupId(groupId)
|
||||
.artifactId(artifactId)
|
||||
.version(version)
|
||||
.resolveTransitiveDependencies(true)
|
||||
.build();
|
||||
|
||||
libraryManager.loadLibrary(lib);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startScheduler(){
|
||||
getProxy().getScheduler().schedule(this, this::fetchServers, 0, 10, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdvancedServerList<Favicon> getCore(){
|
||||
return core;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getFolderPath(){
|
||||
return getDataFolder().toPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginLogger getPluginLogger(){
|
||||
return logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaviconHandler<Favicon> getFaviconHandler(){
|
||||
if(faviconHandler == null)
|
||||
faviconHandler = new FaviconHandler<>(core);
|
||||
|
||||
return faviconHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandManager<CmdSender> getCommandManager(){
|
||||
return handler.commandHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlatformInfo(){
|
||||
return getProxy().getName() + " " + getProxy().getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLoader(){
|
||||
return "bungeecord";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPluginEnabled(String plugin){
|
||||
return getProxy().getPluginManager().getPlugin(plugin) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Favicon createFavicon(BufferedImage image) throws IllegalArgumentException{
|
||||
return Favicon.create(image);
|
||||
}
|
||||
|
||||
public BungeeAudiences getAudiences(){
|
||||
return audiences;
|
||||
}
|
||||
|
||||
public int getOnlinePlayers(ServerInfo server){
|
||||
List<ProxiedPlayer> players = new ArrayList<>(server == null ? getProxy().getPlayers() : server.getPlayers());
|
||||
|
||||
// Exclude players when PremiumVanish is enabled and player is hidden.
|
||||
if(getProxy().getPluginManager().getPlugin("PremiumVanish") != null){
|
||||
players.removeIf(BungeeVanishAPI::isInvisible);
|
||||
}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(){
|
||||
return fetchedServers;
|
||||
}
|
||||
|
||||
private boolean loadLibs(){
|
||||
try{
|
||||
if(libraryManager == null){
|
||||
libraryManager = new BungeeLibraryManager(this);
|
||||
libraryManager.addRepository("https://repo.papermc.io/repository/maven-public");
|
||||
}
|
||||
|
||||
libraryManager.configureFromJSON("dependencies.json");
|
||||
|
||||
return true;
|
||||
}catch(Exception ex){
|
||||
getLogger().log(Level.WARNING, "Encountered an Exception while trying to load dependencies.", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchServers(){
|
||||
fetchedServers.clear();
|
||||
getProxy().getServers().forEach((name, server) -> server.ping((ping, throwable) -> {
|
||||
if(throwable != null)
|
||||
return;
|
||||
|
||||
fetchedServers.put(name, ping);
|
||||
}));
|
||||
}
|
||||
}
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.bungeecord.commands;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
|
||||
import net.kyori.adventure.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{
|
||||
|
||||
private final CommandSender sender;
|
||||
private final BungeeAudiences bungeeAudiences;
|
||||
|
||||
public BungeeCmdSender(CommandSender sender, BungeeAudiences bungeeAudiences){
|
||||
this.sender = sender;
|
||||
this.bungeeAudiences = bungeeAudiences;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* 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.bungeecord.commands;
|
||||
|
||||
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandManagerInterface;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.SenderMapper;
|
||||
import org.incendo.cloud.bungee.BungeeCommandManager;
|
||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||
|
||||
public class BungeeCommandManagerInterface implements CommandManagerInterface<CommandSender>{
|
||||
|
||||
private final BungeeCordCore plugin;
|
||||
|
||||
public BungeeCommandManagerInterface(BungeeCordCore plugin){
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
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.getAudiences()),
|
||||
sender -> ((BungeeCmdSender)sender).sender()
|
||||
);
|
||||
}
|
||||
}
|
||||
-219
@@ -1,219 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.bungeecord.listeners;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.events.GenericServerListEvent;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
|
||||
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
|
||||
import ch.andre601.advancedserverlist.bungeecord.objects.impl.BungeePlayerImpl;
|
||||
import ch.andre601.advancedserverlist.bungeecord.objects.impl.BungeeProxyImpl;
|
||||
import ch.andre601.advancedserverlist.core.compat.maintenance.MaintenanceUtil;
|
||||
import ch.andre601.advancedserverlist.core.compat.papi.PAPIUtil;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.events.GenericEventWrapper;
|
||||
import ch.andre601.advancedserverlist.core.objects.CachedPlayer;
|
||||
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
|
||||
import ch.andre601.advancedserverlist.core.profiles.replacer.StringReplacer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||
import net.md_5.bungee.api.Favicon;
|
||||
import net.md_5.bungee.api.ServerPing;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.ProxyPingEvent;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class BungeeEventWrapper implements GenericEventWrapper<Favicon, BungeePlayerImpl>{
|
||||
|
||||
private final BungeeCordCore plugin;
|
||||
private final ProxyPingEvent event;
|
||||
private final ServerPing.Protocol protocol;
|
||||
private final ServerPing ping;
|
||||
|
||||
public BungeeEventWrapper(BungeeCordCore plugin, ProxyPingEvent event){
|
||||
this.plugin = plugin;
|
||||
this.event = event;
|
||||
this.protocol = event.getResponse().getVersion();
|
||||
this.ping = event.getResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericServerListEvent callEvent(ProfileEntry entry){
|
||||
PreServerListSetEventImpl event = new PreServerListSetEventImpl(entry);
|
||||
plugin.getProxy().getPluginManager().callEvent(event);
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxPlayers(int maxPlayers){
|
||||
ping.getPlayers().setMax(maxPlayers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnlinePlayers(int onlinePlayers){
|
||||
ping.getPlayers().setOnline(onlinePlayers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMotd(Component component){
|
||||
ping.setDescriptionComponent(new TextComponent(BungeeComponentSerializer.get().serialize(component)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hidePlayers(){
|
||||
ping.setPlayers(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerCount(String name){
|
||||
protocol.setName(name);
|
||||
protocol.setProtocol(-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayers(List<String> lines, BungeePlayerImpl player, GenericServer server){
|
||||
ServerPing.PlayerInfo[] players = new ServerPing.PlayerInfo[lines.size()];
|
||||
|
||||
for(int i = 0; i < players.length; i++){
|
||||
String parsed = ComponentParser.text(lines.get(i))
|
||||
.modifyText(text -> StringReplacer.replace(text, player, server))
|
||||
.modifyText(text -> parsePAPIPlaceholders(text, player))
|
||||
.toString();
|
||||
|
||||
ServerPing.PlayerInfo pi = new ServerPing.PlayerInfo(parsed, UUID.randomUUID());
|
||||
players[i] = pi;
|
||||
}
|
||||
|
||||
if(players.length > 0)
|
||||
ping.getPlayers().setSample(players);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayersHidden(){
|
||||
ping.getPlayers().setSample(new ServerPing.PlayerInfo[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFavicon(Favicon favicon){
|
||||
ping.setFavicon(favicon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultFavicon(){
|
||||
ping.setFavicon(ping.getFaviconObject());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateEvent(){
|
||||
this.ping.setVersion(this.protocol);
|
||||
this.event.setResponse(this.ping);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInvalidProtocol(){
|
||||
return protocol == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMaintenanceModeActive(){
|
||||
if(plugin.getProxy().getPluginManager().getPlugin("Maintenance") == null)
|
||||
return false;
|
||||
|
||||
return MaintenanceUtil.get().isMaintenanceEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion(){
|
||||
return event.getConnection().getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOnlinePlayers(){
|
||||
return plugin.getOnlinePlayers(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxPlayers(){
|
||||
return ping.getPlayers().getMax();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlayerIP(){
|
||||
return ((InetSocketAddress)event.getConnection().getSocketAddress()).getHostString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parsePAPIPlaceholders(String text, BungeePlayerImpl player){
|
||||
if(plugin.getProxy().getPluginManager().getPlugin("PAPIProxyBridge") == null)
|
||||
return text;
|
||||
|
||||
if(!PAPIUtil.get().isCompatible())
|
||||
return text;
|
||||
|
||||
String server = PAPIUtil.get().getServer();
|
||||
if(server == null || server.isEmpty())
|
||||
return text;
|
||||
|
||||
ServerInfo serverInfo = plugin.getProxy().getServerInfo(server);
|
||||
if(serverInfo == null || serverInfo.getPlayers().isEmpty())
|
||||
return text;
|
||||
|
||||
ProxiedPlayer carrier = PAPIUtil.get().getPlayer(serverInfo.getPlayers());
|
||||
if(carrier == null)
|
||||
return text;
|
||||
|
||||
return PAPIUtil.get().parse(text, carrier.getUniqueId(), player.getUUID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVirtualHost(){
|
||||
return this.resolveHost(event.getConnection().getVirtualHost());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginCore<Favicon> getPlugin(){
|
||||
return plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BungeePlayerImpl createPlayer(CachedPlayer player, int protocol){
|
||||
return new BungeePlayerImpl(player, protocol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GenericServer createGenericServer(int playersOnline, int playersMax, String host){
|
||||
Map<String, ServerInfo> servers = plugin.getProxy().getServers();
|
||||
|
||||
return new BungeeProxyImpl(servers, playersOnline, playersMax, host);
|
||||
}
|
||||
}
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.bungeecord.listeners;
|
||||
|
||||
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
|
||||
import ch.andre601.advancedserverlist.bungeecord.commands.BungeeCmdSender;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.PostLoginEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
public class PlayerJoinEventListener implements Listener{
|
||||
private final BungeeCordCore plugin;
|
||||
|
||||
public PlayerJoinEventListener(BungeeCordCore plugin){
|
||||
this.plugin = plugin;
|
||||
plugin.getProxy().getPluginManager().registerListener(plugin, this);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onJoin(PostLoginEvent event){
|
||||
InetSocketAddress address = (InetSocketAddress)event.getPlayer().getPendingConnection().getSocketAddress();
|
||||
ProxiedPlayer player = event.getPlayer();
|
||||
|
||||
plugin.getCore().getPlayerHandler().addPlayer(address.getHostString(), player.getName(), player.getUniqueId());
|
||||
|
||||
if(player.hasPermission("advancedserverlist.admin") || player.hasPermission("advancedserverlist.updatecheck")){
|
||||
if(plugin.getAudiences() == null || plugin.getCore().getUpdateChecker() == null)
|
||||
return;
|
||||
|
||||
BungeeCmdSender sender = new BungeeCmdSender(player, plugin.getAudiences());
|
||||
|
||||
plugin.getCore().getUpdateChecker().performCachedUpdateCheck(sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.bungeecord.listeners;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.bungeecord.events.PreServerListSetEvent;
|
||||
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
|
||||
|
||||
public class PreServerListSetEventImpl extends PreServerListSetEvent{
|
||||
|
||||
public PreServerListSetEventImpl(ProfileEntry entry){
|
||||
super(entry);
|
||||
}
|
||||
}
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.bungeecord.listeners;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.bungeecord.events.PostServerListSetEvent;
|
||||
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
|
||||
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
|
||||
import ch.andre601.advancedserverlist.core.events.PingEventHandler;
|
||||
import net.md_5.bungee.api.event.ProxyPingEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
|
||||
public class ProxyPingEventListener implements Listener{
|
||||
|
||||
private final BungeeCordCore plugin;
|
||||
|
||||
public ProxyPingEventListener(BungeeCordCore plugin){
|
||||
this.plugin = plugin;
|
||||
plugin.getProxy().getPluginManager().registerListener(plugin, this);
|
||||
}
|
||||
|
||||
@EventHandler(priority = 90) // Maintenance has Event Priority 80, so we need to be AFTER it.
|
||||
public void onProxyPing(ProxyPingEvent event){
|
||||
ProfileEntry entry = PingEventHandler.handleEvent(new BungeeEventWrapper(plugin, event));
|
||||
|
||||
plugin.getProxy().getPluginManager().callEvent(new PostServerListSetEvent(entry));
|
||||
}
|
||||
}
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2023 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.bungeecord.logging;
|
||||
|
||||
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
|
||||
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class BungeeLogger implements PluginLogger{
|
||||
|
||||
private final BungeeCordCore plugin;
|
||||
private final Logger logger;
|
||||
|
||||
public BungeeLogger(BungeeCordCore plugin){
|
||||
this.plugin = plugin;
|
||||
this.logger = plugin.getLogger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(Class<?> clazz, String msg, Object... args){
|
||||
if(plugin.isDebugEnabled())
|
||||
info("[DEBUG] [" + clazz.getSimpleName() + "] " + msg, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debugWarn(Class<?> clazz, String msg, Object... args){
|
||||
if(plugin.isDebugEnabled())
|
||||
warn("[DEBUG] [" + clazz.getSimpleName() + "] " + msg, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String msg, Object... args){
|
||||
logger.info(ComponentParser.textFormatted(msg, args).toStringStriped());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String msg, Object... args){
|
||||
logger.warning(ComponentParser.textFormatted(msg, args).toStringStriped());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String msg, Throwable throwable){
|
||||
logger.log(Level.WARNING, ComponentParser.text(msg).toStringStriped(), throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String msg, Throwable throwable, Object... args){
|
||||
logger.log(Level.WARNING, ComponentParser.textFormatted(msg, args).toStringStriped(), throwable);
|
||||
}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.bungeecord.objects.impl;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.bungeecord.objects.BungeePlayer;
|
||||
import ch.andre601.advancedserverlist.core.objects.CachedPlayer;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public record BungeePlayerImpl(CachedPlayer player, int protocol) implements BungeePlayer{
|
||||
|
||||
@Override
|
||||
public String getName(){
|
||||
return player.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocol(){
|
||||
return protocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID(){
|
||||
return player.uuid();
|
||||
}
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.bungeecord.objects.impl;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.bungeecord.objects.BungeeProxy;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public record BungeeProxyImpl(Map<String, ServerInfo> servers, int playersOnline, int playersMax, String host) implements BungeeProxy{
|
||||
@Override
|
||||
public Map<String, ServerInfo> getServers(){
|
||||
return servers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayersOnline(){
|
||||
return playersOnline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayersMax(){
|
||||
return playersMax;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHost(){
|
||||
return host;
|
||||
}
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.bungeecord.objects.placeholders;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class BungeePlayerPlaceholders extends PlaceholderProvider{
|
||||
|
||||
private BungeePlayerPlaceholders(){
|
||||
super("player");
|
||||
}
|
||||
|
||||
public static BungeePlayerPlaceholders init(){
|
||||
return new BungeePlayerPlaceholders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
|
||||
return switch(placeholder.toLowerCase(Locale.ROOT)){
|
||||
case "name" -> player.getName();
|
||||
case "protocol" -> String.valueOf(player.getProtocol());
|
||||
case "uuid" -> String.valueOf(player.getUUID());
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
package ch.andre601.advancedserverlist.bungeecord.objects.placeholders;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
|
||||
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||
import net.md_5.bungee.api.ServerPing;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
|
||||
public class BungeeProxyPlaceholders extends PlaceholderProvider{
|
||||
|
||||
private final BungeeCordCore plugin;
|
||||
|
||||
private BungeeProxyPlaceholders(BungeeCordCore plugin){
|
||||
super("proxy");
|
||||
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public static BungeeProxyPlaceholders init(BungeeCordCore plugin){
|
||||
return new BungeeProxyPlaceholders(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
|
||||
String[] args = placeholder.split("\\s", 2);
|
||||
if(args.length < 2)
|
||||
return null;
|
||||
|
||||
ServerPing ping = plugin.getFetchedServers().get(args[1]);
|
||||
|
||||
return switch(args[0]){
|
||||
case "status" -> ping == null ? "offline" : "online";
|
||||
case "motd" -> {
|
||||
if(ping == null)
|
||||
yield "none";
|
||||
|
||||
BaseComponent[] components = new BaseComponent[]{ping.getDescriptionComponent()};
|
||||
Component component = BungeeComponentSerializer.get().deserialize(components);
|
||||
|
||||
yield ComponentParser.toMMString(component);
|
||||
}
|
||||
case "players" -> {
|
||||
if(ping == null)
|
||||
yield "0";
|
||||
|
||||
yield String.valueOf(ping.getPlayers().getOnline());
|
||||
}
|
||||
case "maxPlayers" -> {
|
||||
if(ping == null)
|
||||
yield "0";
|
||||
|
||||
yield String.valueOf(ping.getPlayers().getMax());
|
||||
}
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
||||
-103
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.bungeecord.objects.placeholders;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
|
||||
import ch.andre601.advancedserverlist.api.bungeecord.objects.BungeeProxy;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
public class BungeeServerPlaceholders extends PlaceholderProvider{
|
||||
|
||||
private final BungeeCordCore plugin;
|
||||
|
||||
private BungeeServerPlaceholders(BungeeCordCore plugin){
|
||||
super("server");
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public static BungeeServerPlaceholders init(BungeeCordCore plugin){
|
||||
return new BungeeServerPlaceholders(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
|
||||
if(!(server instanceof BungeeProxy proxy))
|
||||
return null;
|
||||
|
||||
String[] args = placeholder.split("\\s", 2);
|
||||
|
||||
return switch(args[0]){
|
||||
case "playersOnline" -> {
|
||||
if(args.length >= 2){
|
||||
String[] servers = args[1].split(",");
|
||||
|
||||
int players = 0;
|
||||
for(String serverName : servers){
|
||||
if(serverName.isEmpty())
|
||||
continue;
|
||||
|
||||
ServerInfo info = proxy.getServers().get(serverName.strip());
|
||||
if(info == null)
|
||||
continue;
|
||||
|
||||
players += plugin.getOnlinePlayers(info);
|
||||
}
|
||||
|
||||
yield String.valueOf(players);
|
||||
}
|
||||
|
||||
yield String.valueOf(proxy.getPlayersOnline());
|
||||
}
|
||||
case "playersMax" -> {
|
||||
// ServerInfo doesn't provide the max players that could join, so we won't allow an extra argument
|
||||
if(args.length >= 2)
|
||||
yield "";
|
||||
|
||||
yield String.valueOf(proxy.getPlayersMax());
|
||||
}
|
||||
case "host" -> {
|
||||
if(args.length > 2)
|
||||
yield "";
|
||||
|
||||
if(args.length == 2 && args[1] != null && !args[1].isEmpty()){
|
||||
ServerInfo info = proxy.getServers().get(args[1]);
|
||||
if(info == null)
|
||||
yield "";
|
||||
|
||||
yield String.valueOf(((InetSocketAddress)info.getSocketAddress()).getHostString());
|
||||
}
|
||||
|
||||
yield proxy.getHost();
|
||||
}
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
name: 'AdvancedServerList'
|
||||
author: 'Andre_601'
|
||||
version: '${plugin.version}'
|
||||
description: '${plugin.description}'
|
||||
|
||||
softDepends:
|
||||
- PAPIProxyBridge
|
||||
- Maintenance
|
||||
- ServerListPlus
|
||||
|
||||
main: 'ch.andre601.advancedserverlist.bungeecord.BungeeCordCore'
|
||||
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"libraries": [
|
||||
{
|
||||
"groupId": "net{}kyori",
|
||||
"artifactId": "adventure-api",
|
||||
"version": "${dependency.adventure-api}",
|
||||
"resolveTransitiveDependencies": true
|
||||
},
|
||||
{
|
||||
"groupId": "net{}kyori",
|
||||
"artifactId": "adventure-text-minimessage",
|
||||
"version": "${dependency.adventure-text-minimessage}",
|
||||
"resolveTransitiveDependencies": true
|
||||
},
|
||||
{
|
||||
"groupId": "net{}kyori",
|
||||
"artifactId": "adventure-platform-bungeecord",
|
||||
"version": "${dependency.adventure-platform-bungeecord}",
|
||||
"resolveTransitiveDependencies": true
|
||||
},
|
||||
{
|
||||
"groupId": "org{}spongepowered",
|
||||
"artifactId": "configurate-yaml",
|
||||
"version": "${dependency.configurate-yaml}",
|
||||
"resolveTransitiveDependencies": true
|
||||
}
|
||||
]
|
||||
}
|
||||
+24
-9
@@ -43,7 +43,7 @@
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
<plugin.config.version>8</plugin.config.version>
|
||||
<plugin.config.version>10</plugin.config.version>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
@@ -79,7 +79,7 @@
|
||||
<dependency>
|
||||
<groupId>ch.andre601</groupId>
|
||||
<artifactId>expressionparser</artifactId>
|
||||
<version>1.6.0</version>
|
||||
<version>1.7.0-b1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -93,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>
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.12.1</version>
|
||||
<version>2.15.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -133,9 +133,11 @@
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>33.4.0-jre</version>
|
||||
<version>33.8.0-jre</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Command Framework -->
|
||||
<dependency>
|
||||
<groupId>org.incendo</groupId>
|
||||
<artifactId>cloud-annotations</artifactId>
|
||||
@@ -155,17 +157,24 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Annotations -->
|
||||
<dependency>
|
||||
<groupId>org.jspecify</groupId>
|
||||
<artifactId>jspecify</artifactId>
|
||||
<version>1.0.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Plugins -->
|
||||
<dependency>
|
||||
<groupId>net.william278</groupId>
|
||||
<artifactId>papiproxybridge</artifactId>
|
||||
<version>1.7.2</version>
|
||||
<version>1.8.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>eu.kennytv.maintenance</groupId>
|
||||
<artifactId>maintenance-api</artifactId>
|
||||
<version>4.2.1</version>
|
||||
<version>5.0.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -174,12 +183,18 @@
|
||||
<version>3.5.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.miniplaceholders</groupId>
|
||||
<artifactId>miniplaceholders-api</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Test dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.11.4</version>
|
||||
<version>5.14.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
@@ -206,7 +221,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<version>3.16.0</version>
|
||||
<configuration>
|
||||
<compilerArgs>
|
||||
<arg>-parameters</arg>
|
||||
|
||||
@@ -28,20 +28,22 @@ package ch.andre601.advancedserverlist.core;
|
||||
import ch.andre601.advancedserverlist.api.AdvancedServerListAPI;
|
||||
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
|
||||
import ch.andre601.advancedserverlist.core.check.UpdateChecker;
|
||||
import ch.andre601.advancedserverlist.core.commands.CommandHandler;
|
||||
import ch.andre601.advancedserverlist.core.commands.*;
|
||||
import ch.andre601.advancedserverlist.core.compat.maintenance.MaintenancePlaceholder;
|
||||
import ch.andre601.advancedserverlist.core.file.FileHandler;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
|
||||
import ch.andre601.advancedserverlist.core.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.conditions.reader.PlaceholderTokenReader;
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.reader.PlaceholderValueReader;
|
||||
import ch.andre601.advancedserverlist.core.profiles.handlers.PlayerHandler;
|
||||
import ch.andre601.expressionparser.DefaultExpressionParserEngine;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.annotations.AnnotationParser;
|
||||
import org.incendo.cloud.description.Description;
|
||||
import org.incendo.cloud.injection.ParameterInjector;
|
||||
import org.incendo.cloud.parser.ParserDescriptor;
|
||||
import org.incendo.cloud.parser.standard.BooleanParser;
|
||||
import org.incendo.cloud.parser.standard.IntegerParser;
|
||||
@@ -52,6 +54,8 @@ 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;
|
||||
@@ -59,7 +63,10 @@ import java.util.Properties;
|
||||
public class AdvancedServerList<F>{
|
||||
|
||||
public static <F> AdvancedServerList<F> init(PluginCore<F> plugin, PlaceholderProvider... placeholders){
|
||||
return new AdvancedServerList<>(plugin, Arrays.asList(placeholders));
|
||||
List<PlaceholderProvider> providers = new ArrayList<>(Arrays.asList(placeholders));
|
||||
providers.add(new DatePlaceholder());
|
||||
|
||||
return new AdvancedServerList<>(plugin, providers);
|
||||
}
|
||||
|
||||
private final PluginCore<F> plugin;
|
||||
@@ -68,7 +75,11 @@ public class AdvancedServerList<F>{
|
||||
private final TextCenterUtil textCenterUtil;
|
||||
|
||||
private static final AdvancedServerListAPI api = AdvancedServerListAPI.get();
|
||||
private final ProfileConditionParser parser = ProfileConditionParser.create();
|
||||
private final DefaultExpressionParserEngine parser = new DefaultExpressionParserEngine.DefaultBuilder()
|
||||
.addDefaults()
|
||||
.addTokenReader(new PlaceholderTokenReader())
|
||||
.addValueReader(new PlaceholderValueReader())
|
||||
.build();
|
||||
|
||||
private UpdateChecker updateChecker;
|
||||
|
||||
@@ -80,7 +91,7 @@ public class AdvancedServerList<F>{
|
||||
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);
|
||||
|
||||
@@ -89,52 +100,52 @@ public class AdvancedServerList<F>{
|
||||
}
|
||||
|
||||
resolveVersion();
|
||||
getPlugin().getPluginLogger().info("Starting <white>AdvancedServerList v%s</white>...", version);
|
||||
plugin().pluginLogger().info("Starting <white>AdvancedServerList v%s</white>...", version);
|
||||
load();
|
||||
}
|
||||
|
||||
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 PlayerHandler getPlayerHandler(){
|
||||
public PlayerHandler playerHandler(){
|
||||
return playerHandler;
|
||||
}
|
||||
|
||||
public String getVersion(){
|
||||
public String version(){
|
||||
return version;
|
||||
}
|
||||
|
||||
public ProfileConditionParser getParser(){
|
||||
public DefaultExpressionParserEngine parser(){
|
||||
return parser;
|
||||
}
|
||||
|
||||
public UpdateChecker getUpdateChecker(){
|
||||
public UpdateChecker updateChecker(){
|
||||
return updateChecker;
|
||||
}
|
||||
|
||||
public TextCenterUtil getTextCenterUtil(){
|
||||
public TextCenterUtil textCenterUtil(){
|
||||
return textCenterUtil;
|
||||
}
|
||||
|
||||
public void disable(){
|
||||
getPlugin().getPluginLogger().info("[-] Saving <white>playercache.json</white> 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().success("AdvancedServerList disabled!");
|
||||
plugin().pluginLogger().success("AdvancedServerList disabled!");
|
||||
}
|
||||
|
||||
public void clearFaviconCache(){
|
||||
@@ -142,67 +153,69 @@ public class AdvancedServerList<F>{
|
||||
}
|
||||
|
||||
public void clearPlayerCache(){
|
||||
getPlayerHandler().clearCache();
|
||||
playerHandler().clearCache();
|
||||
}
|
||||
|
||||
private void load(){
|
||||
if(!getFileHandler().loadConfig()){
|
||||
getPlugin().getPluginLogger().failure("Unable to load <white>config.yml</white>! 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 <white>config.yml</white>. Attempting to migrate...");
|
||||
if(getFileHandler().migrateConfig()){
|
||||
getPlugin().getPluginLogger().success("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().failure("Couldn't migrate <white>config.yml</white>! Check previous lines for errors.");
|
||||
plugin().pluginLogger().failure("Couldn't migrate <white>config.yml</white>! Check previous lines for errors.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(getFileHandler().getBool(true, "printBanner")){
|
||||
if(fileHandler().getBool(true, "printBanner")){
|
||||
printBanner();
|
||||
}
|
||||
|
||||
getPlugin().getPluginLogger().info("Platform: <white>%s</white>", plugin.getPlatformInfo());
|
||||
getPlugin().getPluginLogger().info("");
|
||||
plugin().pluginLogger().info("Platform: <white>%s</white>", plugin.platformInfo());
|
||||
plugin().pluginLogger().info("");
|
||||
|
||||
if(getFileHandler().loadProfiles()){
|
||||
getPlugin().getPluginLogger().success("Successfully loaded <white>%d</white> profile(s)!", getFileHandler().getProfiles().size());
|
||||
if(fileHandler().loadProfiles()){
|
||||
plugin().pluginLogger().success("Successfully loaded <white>%d</white> profile(s)!", fileHandler().profiles().size());
|
||||
}else{
|
||||
getPlugin().getPluginLogger().failure("Unable to load profiles! Check previous lines for errors.");
|
||||
plugin().pluginLogger().failure("Unable to load profiles! Check previous lines for errors.");
|
||||
return;
|
||||
}
|
||||
|
||||
loadCommands();
|
||||
|
||||
plugin.loadEvents();
|
||||
getPlayerHandler().load();
|
||||
playerHandler().load();
|
||||
plugin.loadMetrics();
|
||||
plugin.startScheduler();
|
||||
|
||||
getPlugin().getPluginLogger().success("<green>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("<#3b90ff> __ _______ ___");
|
||||
getPlugin().getPluginLogger().info("<#3b90ff> /\\ \\ / ____|_| | |");
|
||||
getPlugin().getPluginLogger().info("<#3b90ff> / \\ \\ | (_(___ | | |");
|
||||
getPlugin().getPluginLogger().info("<#3b90ff> / /\\ \\ \\ \\___ \\ \\| | |");
|
||||
getPlugin().getPluginLogger().info("<#3b90ff> / ____ \\ \\ ____) | | |_|____");
|
||||
getPlugin().getPluginLogger().info("<#3b90ff>/_/_/ \\_\\_\\_____/_/|______|_|");
|
||||
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,24 +224,30 @@ public class AdvancedServerList<F>{
|
||||
}
|
||||
|
||||
private void addPlaceholder(PlaceholderProvider placeholderProvider){
|
||||
AdvancedServerList.getApi().addPlaceholderProvider(placeholderProvider);
|
||||
AdvancedServerList.api().addPlaceholderProvider(placeholderProvider);
|
||||
}
|
||||
|
||||
private void loadCommands(){
|
||||
getPlugin().loadFaviconHandler(this);
|
||||
plugin().loadFaviconHandler(this);
|
||||
|
||||
CommandManager<CmdSender> commandManager = plugin.getCommandManager();
|
||||
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));
|
||||
annotationParser.parse(
|
||||
CmdClearCache.create(this),
|
||||
CmdHelp.create(commandManager),
|
||||
CmdMigrate.create(this),
|
||||
CmdProfiles.create(this),
|
||||
CmdReload.create(this)
|
||||
);
|
||||
|
||||
for(String option : CommandHandler.PROFILE_OPTIONS){
|
||||
for(String option : CmdProfiles.ProfileOption.list()){
|
||||
commandManager.command(
|
||||
commandManager.commandBuilder("advancedserverlist", "asl")
|
||||
.literal("profiles")
|
||||
@@ -238,7 +257,7 @@ public class AdvancedServerList<F>{
|
||||
StringParser.stringParser(),
|
||||
Description.of("The profile to edit."),
|
||||
SuggestionProvider.suggesting(
|
||||
getFileHandler().getProfiles().stream()
|
||||
fileHandler().profiles().stream()
|
||||
.map(ServerListProfile::file)
|
||||
.map(Suggestion::suggestion)
|
||||
.toList()
|
||||
@@ -252,13 +271,15 @@ public class AdvancedServerList<F>{
|
||||
.permission(
|
||||
Permission.anyOf(
|
||||
Permission.permission("advancedserverlist.admin"),
|
||||
Permission.permission("advancedserverlist.command.profiles")
|
||||
Permission.permission("advancedserverlist.command.profiles"),
|
||||
Permission.permission("advancedserverlist.command.profiles.set")
|
||||
)
|
||||
)
|
||||
.meta(CommandHandler.COMMAND_TYPE, CommandType.Type.ALL)
|
||||
);
|
||||
}
|
||||
|
||||
getPlugin().getPluginLogger().success("Loaded Command <white>/advancedserverlist</white>!");
|
||||
plugin().pluginLogger().success("Loaded Command <white>/advancedserverlist</white>!");
|
||||
}
|
||||
|
||||
private ParserDescriptor<CmdSender, ?> descriptor(String option){
|
||||
@@ -283,4 +304,44 @@ public class AdvancedServerList<F>{
|
||||
default -> Description.of("The Value to use for this option. Leave empty to reset.");
|
||||
};
|
||||
}
|
||||
|
||||
// Adding some seasonal messages
|
||||
private void seasonalText(){
|
||||
LocalDate date = LocalDate.now();
|
||||
|
||||
switch(date.getMonth()){
|
||||
case JANUARY -> {
|
||||
if(date.getDayOfMonth() == 1){
|
||||
plugin().pluginLogger().info("Happy new Year!");
|
||||
plugin().pluginLogger().info("");
|
||||
}
|
||||
}
|
||||
case JUNE -> {
|
||||
plugin().pluginLogger().info("Happy Pride Month!");
|
||||
plugin().pluginLogger().info("");
|
||||
}
|
||||
case AUGUST -> {
|
||||
if(date.getDayOfMonth() == 1){
|
||||
plugin().pluginLogger().info("Happy Birthday Switzerland!");
|
||||
plugin().pluginLogger().info("");
|
||||
}
|
||||
}
|
||||
case OCTOBER -> {
|
||||
if(date.getDayOfMonth() == 31){
|
||||
plugin().pluginLogger().info("Happy Halloween!");
|
||||
plugin().pluginLogger().info("");
|
||||
}
|
||||
}
|
||||
case DECEMBER -> {
|
||||
if(date.getDayOfMonth() == 13){
|
||||
plugin().pluginLogger().info("Happy Birthday Andre_601!");
|
||||
plugin().pluginLogger().info("");
|
||||
}else
|
||||
if(date.getDayOfMonth() > 23 && date.getDayOfMonth() < 27){
|
||||
plugin().pluginLogger().info("Merry Christmas!");
|
||||
plugin().pluginLogger().info("");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,13 +25,13 @@
|
||||
|
||||
package ch.andre601.advancedserverlist.core.check;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
public class UpdateCheckThread implements ThreadFactory{
|
||||
@Override
|
||||
public Thread newThread(@NotNull Runnable r){
|
||||
public Thread newThread(@NonNull Runnable r){
|
||||
Thread t = new Thread(r, "AdvancedServerList-UpdateThread");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
|
||||
@@ -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'>" +
|
||||
@@ -103,7 +103,7 @@ public class UpdateChecker{
|
||||
if(result == 1){
|
||||
sender.sendPrefixedMsg(
|
||||
"Your version (<white>%</white>) is newer than what is available on Modrinth (<white>%s</white>).",
|
||||
core.getVersion(),
|
||||
core.version(),
|
||||
version.versionNumber()
|
||||
);
|
||||
sender.sendPrefixedMsg("Are you using a Development Version?");
|
||||
@@ -137,14 +137,14 @@ public class UpdateChecker{
|
||||
|
||||
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.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.getVersion(),
|
||||
core.version(),
|
||||
version.versionNumber()
|
||||
);
|
||||
logger.info(" Are you using a Development Version?");
|
||||
@@ -163,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);
|
||||
}
|
||||
@@ -173,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);
|
||||
@@ -193,7 +193,7 @@ public class UpdateChecker{
|
||||
logger.warn("=======================================================================");
|
||||
logger.warn("You are running an outdated version of AdvancedServerList!");
|
||||
logger.warn("");
|
||||
logger.warn("Your version: <white>%s</white>", core.getVersion());
|
||||
logger.warn("Your version: <white>%s</white>", core.version());
|
||||
logger.warn("Modrinth: <white>%s</white>", version.versionNumber());
|
||||
logger.warn("");
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package ch.andre601.advancedserverlist.core.commands;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
|
||||
import org.incendo.cloud.annotations.Argument;
|
||||
import org.incendo.cloud.annotations.Command;
|
||||
import org.incendo.cloud.annotations.CommandDescription;
|
||||
import org.incendo.cloud.annotations.Permission;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
|
||||
@Command("advancedserverlist|asl")
|
||||
@CommandDescription("Main Command of the plugin")
|
||||
@Permission("advancedserverlist.admin")
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public record CmdClearCache(AdvancedServerList<?> core){
|
||||
|
||||
public static CmdClearCache create(AdvancedServerList<?> core){
|
||||
return new CmdClearCache(core);
|
||||
}
|
||||
|
||||
@Command("clearcache [target]")
|
||||
@CommandDescription("Cleares the Favicon and/or Player Cache.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.clearcache"})
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public void handle(
|
||||
CmdSender sender,
|
||||
@Nullable @Argument(description = "The type of Cache to clean. Defaults to ALL.") CacheTarget target
|
||||
){
|
||||
if(target == null)
|
||||
target = CacheTarget.ALL;
|
||||
|
||||
sender.sendPrefixedMsg("Clearing cache...");
|
||||
|
||||
if(target == CacheTarget.ALL || target == CacheTarget.FAVICON){
|
||||
core().clearFaviconCache();
|
||||
sender.sendPrefixedMsg("Cleared Favicon Cache.");
|
||||
}
|
||||
|
||||
if(target == CacheTarget.ALL || target == CacheTarget.PLAYER){
|
||||
core().clearPlayerCache();
|
||||
sender.sendPrefixedMsg("Cleared Player Cache.");
|
||||
}
|
||||
|
||||
sender.sendPrefixedMsg("<green>Cache clearing complete!");
|
||||
}
|
||||
|
||||
public enum CacheTarget{
|
||||
ALL,
|
||||
FAVICON,
|
||||
PLAYER
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package ch.andre601.advancedserverlist.core.commands;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.annotation.specifier.Greedy;
|
||||
import org.incendo.cloud.annotations.Argument;
|
||||
import org.incendo.cloud.annotations.Command;
|
||||
import org.incendo.cloud.annotations.CommandDescription;
|
||||
import org.incendo.cloud.annotations.Permission;
|
||||
import org.incendo.cloud.annotations.suggestion.Suggestions;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
import org.incendo.cloud.help.result.CommandEntry;
|
||||
import org.incendo.cloud.minecraft.extras.MinecraftHelp;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Command("advancedserverlist|asl")
|
||||
@CommandDescription("Main Command of the plugin")
|
||||
@Permission("advancedserverlist.admin")
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public record CmdHelp(CommandManager<CmdSender> manager, MinecraftHelp<CmdSender> help){
|
||||
|
||||
public static CmdHelp create(CommandManager<CmdSender> manager){
|
||||
MinecraftHelp<CmdSender> help = MinecraftHelp.<CmdSender>builder()
|
||||
.commandManager(manager)
|
||||
.audienceProvider(CmdSender::audience)
|
||||
.commandPrefix("/asl help")
|
||||
.colors(MinecraftHelp.helpColors(
|
||||
NamedTextColor.WHITE, NamedTextColor.AQUA, NamedTextColor.WHITE, NamedTextColor.GRAY, NamedTextColor.DARK_GRAY
|
||||
))
|
||||
.build();
|
||||
|
||||
return new CmdHelp(manager, help);
|
||||
}
|
||||
|
||||
@Command("help [query]")
|
||||
@CommandDescription("Displays all commands or provides info about a specific one.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.help"})
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public void handle(
|
||||
CmdSender sender,
|
||||
@Nullable @Argument(description = "Search Query for a specific command", suggestions = "help") @Greedy String query
|
||||
){
|
||||
this.help.queryCommands(query == null ? "" : query, sender);
|
||||
}
|
||||
|
||||
@Suggestions("help")
|
||||
public List<String> helpQueries(CommandContext<CmdSender> context) {
|
||||
return this.manager.createHelpHandler()
|
||||
.queryRootIndex(context.sender())
|
||||
.entries()
|
||||
.stream()
|
||||
.filter(entry -> context.hasPermission(entry.command().commandPermission()))
|
||||
.filter(entry -> {
|
||||
CommandType.Type type = entry.command().commandMeta().getOrDefault(CommandHandler.COMMAND_TYPE, CommandType.Type.ALL);
|
||||
|
||||
return type == CommandType.Type.ALL || type == CommandType.Type.PLAYER_ONLY && context.sender().isPlayer();
|
||||
})
|
||||
.map(CommandEntry::syntax)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package ch.andre601.advancedserverlist.core.commands;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
|
||||
import ch.andre601.advancedserverlist.core.migration.minimotd.MiniMOTDConfigMigrator;
|
||||
import ch.andre601.advancedserverlist.core.migration.serverlistplus.SLPConfigMigrator;
|
||||
import org.incendo.cloud.annotations.Argument;
|
||||
import org.incendo.cloud.annotations.Command;
|
||||
import org.incendo.cloud.annotations.CommandDescription;
|
||||
import org.incendo.cloud.annotations.Permission;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@Command("advancedserverlist|asl")
|
||||
@CommandDescription("Main Command of the plugin")
|
||||
@Permission("advancedserverlist.admin")
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public record CmdMigrate(AdvancedServerList<?> core){
|
||||
|
||||
public static CmdMigrate create(AdvancedServerList<?> core){
|
||||
return new CmdMigrate(core);
|
||||
}
|
||||
|
||||
@Command("migrate <plugin>")
|
||||
@CommandDescription("Migrates Profiles from supported Plugins to ASL.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.migrate"})
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public void handle(
|
||||
CmdSender sender,
|
||||
@NonNull @Argument(description = "Name of the plugin to migrate from") MigratablePlugin plugin
|
||||
){
|
||||
if(plugin == MigratablePlugin.SERVERLISTPLUS){
|
||||
if(!core().plugin().isPluginEnabled("ServerListPlus")){
|
||||
sender.sendErrorMsg("<red>ServerListPlus not enabled.");
|
||||
sender.sendErrorMsg("<red>The plugin needs to be enabled for the migration to work.");
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendPrefixedMsg("Migrating from ServerListPlus...");
|
||||
|
||||
int migrated = SLPConfigMigrator.migrate(core(), sender);
|
||||
if(migrated == 0){
|
||||
sender.sendErrorMsg("<red>Couldn't migrate any profiles from ServerListPlus.");
|
||||
sender.sendErrorMsg("<red>See the server console for any errors.");
|
||||
return;
|
||||
}else{
|
||||
sender.sendPrefixedMsg("<green>Successfully migrated </green>%d<green> Profile(s)!", migrated);
|
||||
}
|
||||
}else
|
||||
if(plugin == MigratablePlugin.MINIMOTD){
|
||||
sender.sendPrefixedMsg("Migrating from MiniMOTD...");
|
||||
|
||||
if(MiniMOTDConfigMigrator.migrate(core(), sender)){
|
||||
sender.sendPrefixedMsg("<green>Successfully migrated config from MiniMOTD!");
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Couldn't migrate config from MiniMOTD.");
|
||||
sender.sendErrorMsg("<red>See the server console for any errors.");
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Received invalid Plugin </red>%s<red>!", plugin.toString());
|
||||
sender.sendErrorMsg("<red>Only the following plugins are supported:");
|
||||
|
||||
for(MigratablePlugin pl : MigratablePlugin.values()){
|
||||
sender.sendErrorMsg("- <click:suggest_command:/asl migrate %s>%s</click>", pl.name().toLowerCase(Locale.ROOT), pl.name().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
}
|
||||
|
||||
sender.sendPrefixedMsg("<green>Run <white><click:suggest_command:/asl reload>/asl reload</click></white> to load the new Profile(s).");
|
||||
}
|
||||
|
||||
public enum MigratablePlugin{
|
||||
SERVERLISTPLUS,
|
||||
MINIMOTD
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,483 @@
|
||||
package ch.andre601.advancedserverlist.core.commands;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.dialogs.IDialog;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.dialogs.IDialogList;
|
||||
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
|
||||
import ch.andre601.advancedserverlist.core.profiles.profile.ProfileSerializer;
|
||||
import org.incendo.cloud.annotations.Argument;
|
||||
import org.incendo.cloud.annotations.Command;
|
||||
import org.incendo.cloud.annotations.CommandDescription;
|
||||
import org.incendo.cloud.annotations.Permission;
|
||||
import org.incendo.cloud.annotations.suggestion.Suggestions;
|
||||
import org.incendo.cloud.context.CommandInput;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
import org.spongepowered.configurate.yaml.NodeStyle;
|
||||
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
@Command("advancedserverlist|asl")
|
||||
@CommandDescription("Main Command of the plugin.")
|
||||
@Permission("advancedserverlist.admin")
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public record CmdProfiles(AdvancedServerList<?> core){
|
||||
|
||||
public static CmdProfiles create(AdvancedServerList<?> core){
|
||||
return new CmdProfiles(core);
|
||||
}
|
||||
|
||||
@Command("profiles add <name>")
|
||||
@CommandDescription("Creates a new Profile with default values applied.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.profiles", "advancedserverlist.command.profiles.add"})
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public void add(
|
||||
CmdSender sender,
|
||||
@NonNull @Argument(description = "Name of the profile to create.") String name
|
||||
){
|
||||
if(name.isEmpty()){
|
||||
sender.sendErrorMsg("<red>Insufficient arguments.");
|
||||
sender.sendErrorMsg("<red>A profile name needs to be provided.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(CommandHandler.profile(core(), name) != null){
|
||||
sender.sendErrorMsg("<red>A profile with the name </red>%s<red> already exists.", name);
|
||||
return;
|
||||
}
|
||||
|
||||
String file = CommandHandler.profileName(name);
|
||||
|
||||
if(core().fileHandler().createFile(file)){
|
||||
sender.sendPrefixedMsg("<green>Successfully created </green>%s<green>!", file);
|
||||
sender.sendPrefixedMsg("<green>Use <white><click:suggest_command:/asl reload>/asl reload</click></white> to load the file.");
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Error while creating new profile.");
|
||||
sender.sendErrorMsg("<red>Check the server console for details.");
|
||||
}
|
||||
}
|
||||
|
||||
@Command("profiles copy <profile> <name>")
|
||||
public void copy(
|
||||
CmdSender sender,
|
||||
@NonNull @Argument(description = "Name of the profile to copy.", suggestions = "profiles") String profile,
|
||||
@NonNull @Argument(description = "Name of the copy to have.") String name
|
||||
){
|
||||
if(profile.isEmpty() || name.isEmpty()){
|
||||
sender.sendErrorMsg("<red>Insufficient arguments.");
|
||||
sender.sendErrorMsg("<red>A profile name and name for the copy need to be provided.");
|
||||
return;
|
||||
}
|
||||
|
||||
String profileName = CommandHandler.profileName(profile);
|
||||
String copyName = CommandHandler.profileName(name);
|
||||
|
||||
ServerListProfile original = CommandHandler.profile(core(), profileName);
|
||||
|
||||
if(original == null){
|
||||
sender.sendErrorMsg("<red>A profile with name </red>%s<red> does not exist.", profile);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean copyExists = CommandHandler.profile(core(), copyName) != null;
|
||||
if(copyExists){
|
||||
sender.sendErrorMsg("<red>A profile with name </red>%s<red> already exists.", name);
|
||||
return;
|
||||
}
|
||||
|
||||
Path profilePath = core().plugin().folderPath().resolve("profiles").resolve(copyName);
|
||||
if(!Files.exists(profilePath)){
|
||||
try{
|
||||
Files.createFile(profilePath);
|
||||
}catch(IOException ex){
|
||||
sender.sendErrorMsg("<red>Encountered an IOException while creating </red>%s<red>.", copyName);
|
||||
sender.sendErrorMsg("<red>Please see the server console for further details.");
|
||||
|
||||
core.plugin().pluginLogger().warn("Encountered IOException while creating file <white>%s</white>", ex, copyName);
|
||||
return;
|
||||
}
|
||||
|
||||
YamlConfigurationLoader loader = YamlConfigurationLoader.builder()
|
||||
.path(profilePath)
|
||||
.indent(2)
|
||||
.nodeStyle(NodeStyle.BLOCK)
|
||||
.defaultOptions(opt -> opt.serializers(builder -> builder.register(ProfileEntry.class, ProfileSerializer.INSTANCE)))
|
||||
.build();
|
||||
|
||||
ConfigurationNode node;
|
||||
try{
|
||||
node = loader.load();
|
||||
}catch(IOException ex){
|
||||
sender.sendErrorMsg("<red>Encountered an IOException while loading file </red>%s<red>.", copyName);
|
||||
sender.sendErrorMsg("<red>Please see the server console for further details.");
|
||||
|
||||
core().plugin().pluginLogger().warn("Encountered IOException while loading file <white>%s</white>.", ex, copyName);
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
node.node("priority").set(original.priority());
|
||||
node.node("condition").set(original.condition());
|
||||
|
||||
node.node("profiles").setList(ProfileEntry.class, original.profiles());
|
||||
|
||||
node.set(ProfileEntry.class, original.defaultProfile());
|
||||
}catch(SerializationException ex){
|
||||
sender.sendErrorMsg("<red>Encountered a SerializationException while copying values from </red>%s<red> to </red>%s<red>.", profileName, copyName);
|
||||
sender.sendErrorMsg("<red>Please see the server console for further details.");
|
||||
|
||||
core().plugin().pluginLogger().warn("<red>Encountered a SerializationException while copying values from </red>%s<red> to </red>%s<red>.", ex, profileName, copyName);
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
loader.save(node);
|
||||
sender.sendPrefixedMsg("<green>Successfully created copy of </green>%s<green> as </green>%s<green>!", profileName, copyName);
|
||||
sender.sendPrefixedMsg("<green>Use <white><click:suggest_command:/asl reload>/asl reload</click></white> to load the file.");
|
||||
}catch(IOException ex){
|
||||
sender.sendErrorMsg("<red>There was an IOException while loading file </red>%s<red>!", copyName);
|
||||
sender.sendErrorMsg("<red>Please see the server console for further details.");
|
||||
|
||||
core.plugin().pluginLogger().warn("Encountered an IOException while loading file <white>%s</white>", ex, copyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Command("profiles edit <profile>")
|
||||
@CommandDescription("Opens a Dialog to edit a specific profile ingame.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.profiles", "advancedserverlist.command.profiles.edit"})
|
||||
@CommandType(CommandType.Type.PLAYER_ONLY)
|
||||
public void edit(
|
||||
CmdSender sender,
|
||||
@NonNull @Argument(description = "Name of the profile to edit.", suggestions = "profiles") String profile
|
||||
){
|
||||
if(!core().plugin().isDialogsSupported()){
|
||||
sender.sendErrorMsg("Dialogs are not supported on this server or proxy version.");
|
||||
return;
|
||||
}
|
||||
|
||||
ServerListProfile slp = CommandHandler.profile(core(), profile);
|
||||
if(slp == null){
|
||||
sender.sendErrorMsg("<red>A profile with name </red>%s<red> does not exist.", profile);
|
||||
return;
|
||||
}
|
||||
|
||||
IDialogList.IDialogListBuilder listBuilder = core().plugin().createDialogListBuilder(slp.file());
|
||||
String warning = """
|
||||
<gold><bold>WARNING!</bold></gold>
|
||||
|
||||
Dialog-based Profile editing is currently an <italic>experimental feature</italic>!
|
||||
Please create backups of your profiles before using this feature and report any bugs and/or glitches you encounter.""";
|
||||
|
||||
listBuilder.appendBody(warning);
|
||||
|
||||
listBuilder.appendDialog(
|
||||
createDialog("Main Settings", warning, slp, slp.defaultProfile(), slp.priority(), slp.condition(), -1)
|
||||
);
|
||||
|
||||
for(int i = 0; i < slp.profiles().size(); i++){
|
||||
listBuilder.appendDialog(
|
||||
createDialog("Profiles #" + (i + 1), warning, slp, slp.profiles().get(i), -1, null, i)
|
||||
);
|
||||
}
|
||||
|
||||
listBuilder.build().show(sender);
|
||||
}
|
||||
|
||||
@Command("profiles info <profile>")
|
||||
@CommandDescription("Provides info about a specific profile.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.profiles", "advancedserverlist.command.profiles.info"})
|
||||
@CommandType(CommandType.Type.PLAYER_ONLY)
|
||||
public void info(
|
||||
CmdSender sender,
|
||||
@NonNull @Argument(description = "Name of the profile to view.", suggestions = "profiles") String profile
|
||||
){
|
||||
if(profile.isEmpty()){
|
||||
sender.sendErrorMsg("<red>Insufficient arguments.");
|
||||
sender.sendErrorMsg("<red>A profile name needs to be provided.");
|
||||
return;
|
||||
}
|
||||
|
||||
ServerListProfile slp = CommandHandler.profile(core(), profile);
|
||||
|
||||
if(slp == null){
|
||||
sender.sendErrorMsg("<red>Unable to find profile with name </red>%s<red>.", profile);
|
||||
sender.sendErrorMsg("<red>Make sure you typed it correctly.");
|
||||
return;
|
||||
}
|
||||
|
||||
ProfileOptionsSender optionsSender = new ProfileOptionsSender(slp.file());
|
||||
|
||||
optionsSender
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Priority", slp.priority())
|
||||
.option(ProfileOption.PRIORITY)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Condition", slp.condition())
|
||||
.option(ProfileOption.CONDITION)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Profiles")
|
||||
.color(slp.profiles().isEmpty() ? ProfileOptionsSender.ColorName.RED : ProfileOptionsSender.ColorName.GREEN)
|
||||
);
|
||||
|
||||
if(!slp.profiles().isEmpty()){
|
||||
for(int i = 0; i < slp.profiles().size(); i++){
|
||||
optionsSender.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("#" + (i + 1), profileHover(slp.profiles().get(i)))
|
||||
.prefix("│ %s─".formatted((i + 1) == slp.profiles().size() ? "└" : "├"))
|
||||
.color(ProfileOptionsSender.ColorName.AQUA)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ProfileEntry entry = slp.defaultProfile();
|
||||
|
||||
optionsSender
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("MOTD", entry.motd())
|
||||
.option(ProfileOption.MOTD)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Favicon", entry.favicon())
|
||||
.option(ProfileOption.FAVICON)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Player count")
|
||||
.prefix("└─")
|
||||
.color(ProfileOptionsSender.ColorName.AQUA)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Hide Player count?", entry.hidePlayersEnabled())
|
||||
.prefix(" ├─")
|
||||
.option(ProfileOption.PLAYERCOUNT_HIDEPLAYERS)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Hide Player count hover?", entry.hidePlayersHoverEnabled())
|
||||
.prefix(" ├─")
|
||||
.option(ProfileOption.PLAYERCOUNT_HIDEPLAYERSHOVER)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Player count hover", entry.players())
|
||||
.prefix(" ├─")
|
||||
.option(ProfileOption.PLAYERCOUNT_HOVER)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Player count text", entry.playerCountText())
|
||||
.prefix(" ├─")
|
||||
.option(ProfileOption.PLAYERCOUNT_TEXT)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Extra Players")
|
||||
.prefix(" ├─")
|
||||
.color(ProfileOptionsSender.ColorName.AQUA)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Enabled?", entry.extraPlayersEnabled())
|
||||
.prefix(" │ ├─")
|
||||
.option(ProfileOption.PLAYERCOUNT_EXTRAPLAYERS_ENABLED)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Amount", entry.extraPlayersCount())
|
||||
.prefix(" │ └─")
|
||||
.option(ProfileOption.PLAYERCOUNT_EXTRAPLAYERS_AMOUNT)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Max Players")
|
||||
.prefix(" ├─")
|
||||
.color(ProfileOptionsSender.ColorName.AQUA)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Enabled?", entry.maxPlayersEnabled())
|
||||
.prefix(" │ ├─")
|
||||
.option(ProfileOption.PLAYERCOUNT_MAXPLAYERS_ENABLED)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Amount", entry.maxPlayersCount())
|
||||
.prefix(" │ └─")
|
||||
.option(ProfileOption.PLAYERCOUNT_MAXPLAYERS_AMOUNT)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Online Players")
|
||||
.prefix(" └─")
|
||||
.color(ProfileOptionsSender.ColorName.AQUA)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Enabled?", entry.onlinePlayersEnabled())
|
||||
.prefix(" ├─")
|
||||
.option(ProfileOption.PLAYERCOUNT_ONLINEPLAYERS_ENABLED)
|
||||
).append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Amount", entry.onlinePlayersCount())
|
||||
.prefix(" └─")
|
||||
.option(ProfileOption.PLAYERCOUNT_ONLINEPLAYERS_AMOUNT)
|
||||
).send(sender);
|
||||
}
|
||||
|
||||
@Command("profiles list")
|
||||
@CommandDescription("Lists all available profiles with priority, conditions and if they are valid.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.profiles", "advancedserverlist.command.profiles.list"})
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public void list(CmdSender sender){
|
||||
sender.sendMsg();
|
||||
sender.sendPrefixedMsg("Available Profiles:");
|
||||
sender.sendMsg("<dark_grey>|");
|
||||
|
||||
List<ServerListProfile> profiles = core().fileHandler().profiles().stream()
|
||||
.sorted(Comparator.comparingInt(ServerListProfile::priority).reversed())
|
||||
.toList();
|
||||
|
||||
if(profiles.isEmpty()){
|
||||
sender.sendMsg("<dark_grey></dark_grey> <i>No profiles found.</i>");
|
||||
}else{
|
||||
for(int i = 0; i < profiles.size(); i++){
|
||||
sender.sendMsg(
|
||||
"<dark_grey>%s</dark_grey> <aqua>" +
|
||||
"<hover:show_text:\"%s\">" +
|
||||
"<click:suggest_command:/asl profiles info %s>" +
|
||||
"%s" +
|
||||
"</click>" +
|
||||
"</hover>" +
|
||||
"</aqua>",
|
||||
(i + 1) == profiles.size() ? "" : "-",
|
||||
hover(profiles.get(i)),
|
||||
profiles.get(i).file(),
|
||||
profiles.get(i).file()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
sender.sendMsg();
|
||||
sender.sendMsg("<grey><i>Hover</i></grey> = Show details.");
|
||||
sender.sendMsg("<grey><i>Click</i></grey> = Get Command.");
|
||||
}
|
||||
|
||||
@Suggestions("profiles")
|
||||
public List<String> profilesSuggestions(CommandInput input){
|
||||
String name = input.readString();
|
||||
List<String> profiles = core().fileHandler().profiles().stream()
|
||||
.map(profile -> profile.file().substring(0, profile.file().lastIndexOf('.')))
|
||||
.toList();
|
||||
if(name.isEmpty())
|
||||
return profiles;
|
||||
|
||||
List<String> matches = new ArrayList<>(profiles.size());
|
||||
for(String str : profiles){
|
||||
if(str.length() >= name.length() && str.regionMatches(true, 0, name, 0, name.length()))
|
||||
matches.add(str);
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
private String hover(ServerListProfile profile){
|
||||
return """
|
||||
<grey>Priority: <white>%d</white>
|
||||
Condition: <white>%s</white>
|
||||
|
||||
Valid? %s""".formatted(
|
||||
profile.priority(),
|
||||
profile.condition() == null || profile.condition().isEmpty() ? "<i>None</i>" : profile.condition(),
|
||||
profile.isInvalidProfile() ? "<red>No</red>" : "<green>Yes</green>"
|
||||
);
|
||||
}
|
||||
|
||||
private String profileHover(ProfileEntry entry){
|
||||
return """
|
||||
<grey>MOTD:
|
||||
%s<reset>
|
||||
|
||||
<grey>Favicon: %s<reset>
|
||||
|
||||
<grey>Hide Player count? %s
|
||||
Hide Player count Hover? %s
|
||||
|
||||
Player count Hover:
|
||||
%s<reset>
|
||||
|
||||
<grey>Player count Text: %s<reset>
|
||||
|
||||
<grey>Extra Players: %s<reset>
|
||||
<grey>Max Players: %s<reset>
|
||||
<grey>Online Players: %s""".formatted(
|
||||
entry.motd() == null || entry.motd().isEmpty() ?
|
||||
"<i><white>None</white></i>" : String.join("<reset>\n", entry.motd()),
|
||||
entry.favicon() == null || entry.favicon().isEmpty() ?
|
||||
"<i><white>None</white></i>" : entry.favicon(),
|
||||
entry.hidePlayersEnabled().getOrDefault(false) ? "<green>Enabled</green>" : "<red>Disabled</red>",
|
||||
entry.hidePlayersHoverEnabled().getOrDefault(false) ? "<green>Enabled</green>" : "<red>Disabled</red>",
|
||||
entry.players().isEmpty() ? "<i><white>None</white></i>" : String.join("<reset>\n", entry.players()),
|
||||
entry.playerCountText() == null || entry.playerCountText().isEmpty() ?
|
||||
"<i><white>None</white></i>" : entry.playerCountText(),
|
||||
entry.extraPlayersEnabled().getOrDefault(false) ? entry.extraPlayersCount() : "<red>Disabled</red>",
|
||||
entry.maxPlayersEnabled().getOrDefault(false) ? entry.maxPlayersCount() : "<red>Disabled</red>",
|
||||
entry.onlinePlayersEnabled().getOrDefault(false) ? entry.onlinePlayersCount() : "<red>Disabled</red>"
|
||||
);
|
||||
}
|
||||
|
||||
private IDialog<?> createDialog(
|
||||
String title,
|
||||
String warning,
|
||||
ServerListProfile slp,
|
||||
ProfileEntry entry,
|
||||
int priority,
|
||||
String condition,
|
||||
int index
|
||||
){
|
||||
IDialog.IDialogBuilder builder = core.plugin().createDialogBuilder(title, slp, index);
|
||||
builder.appendBody(warning);
|
||||
|
||||
if(index < 0){
|
||||
builder.appendNumberRange(ProfileOption.PRIORITY, "Priority", priority)
|
||||
.appendTextInput(ProfileOption.CONDITION, "Condition", condition, 100);
|
||||
}
|
||||
|
||||
builder.appendTextbox(ProfileOption.MOTD, "MOTD", entry.motd(), 200, 2)
|
||||
.appendTextInput(ProfileOption.FAVICON, "Favicon", entry.favicon(), 100)
|
||||
.appendBoolean(ProfileOption.PLAYERCOUNT_HIDEPLAYERS, "Hide Player Count", entry.hidePlayersEnabled())
|
||||
.appendBoolean(ProfileOption.PLAYERCOUNT_HIDEPLAYERSHOVER, "Hide Player Count Hover", entry.hidePlayersHoverEnabled())
|
||||
.appendTextbox(ProfileOption.PLAYERCOUNT_HOVER, "Player Count Hover", entry.players(), 200, 10)
|
||||
.appendTextInput(ProfileOption.PLAYERCOUNT_TEXT, "Player Count Text", entry.playerCountText(), 100)
|
||||
.appendBoolean(ProfileOption.PLAYERCOUNT_EXTRAPLAYERS_ENABLED, "Enable Extra Players", entry.extraPlayersEnabled())
|
||||
.appendTextInput(ProfileOption.PLAYERCOUNT_EXTRAPLAYERS_AMOUNT, "Extra Players Count", entry.extraPlayersCount(), 100)
|
||||
.appendBoolean(ProfileOption.PLAYERCOUNT_MAXPLAYERS_ENABLED, "Enable Max Players", entry.maxPlayersEnabled())
|
||||
.appendTextInput(ProfileOption.PLAYERCOUNT_MAXPLAYERS_AMOUNT, "Max Players Count", entry.maxPlayersCount(), 100)
|
||||
.appendBoolean(ProfileOption.PLAYERCOUNT_ONLINEPLAYERS_ENABLED, "Enable Online Players", entry.onlinePlayersEnabled())
|
||||
.appendTextInput(ProfileOption.PLAYERCOUNT_ONLINEPLAYERS_AMOUNT, "Online Players Count", entry.onlinePlayersCount(), 100);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public enum ProfileOption{
|
||||
PRIORITY,
|
||||
CONDITION,
|
||||
MOTD,
|
||||
FAVICON,
|
||||
PLAYERCOUNT_HIDEPLAYERS,
|
||||
PLAYERCOUNT_HIDEPLAYERSHOVER,
|
||||
PLAYERCOUNT_HOVER,
|
||||
PLAYERCOUNT_TEXT,
|
||||
PLAYERCOUNT_EXTRAPLAYERS_ENABLED,
|
||||
PLAYERCOUNT_EXTRAPLAYERS_AMOUNT,
|
||||
PLAYERCOUNT_MAXPLAYERS_ENABLED,
|
||||
PLAYERCOUNT_MAXPLAYERS_AMOUNT,
|
||||
PLAYERCOUNT_ONLINEPLAYERS_ENABLED,
|
||||
PLAYERCOUNT_ONLINEPLAYERS_AMOUNT;
|
||||
|
||||
public static List<String> list(){
|
||||
return Arrays.stream(values())
|
||||
.map(ProfileOption::toString)
|
||||
.toList();
|
||||
}
|
||||
|
||||
public static ProfileOption fromString(String input){
|
||||
for(ProfileOption option : values()){
|
||||
if(option.toString().equalsIgnoreCase(input))
|
||||
return option;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return this.name().toLowerCase(Locale.ROOT).replace("_", ".");
|
||||
}
|
||||
|
||||
public String toDialogKey(){
|
||||
return this.name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package ch.andre601.advancedserverlist.core.commands;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
|
||||
import org.incendo.cloud.annotations.Command;
|
||||
import org.incendo.cloud.annotations.CommandDescription;
|
||||
import org.incendo.cloud.annotations.Permission;
|
||||
|
||||
|
||||
@Command("advancedserverlist|asl")
|
||||
@CommandDescription("Main Command of the plugin")
|
||||
@Permission("advancedserverlist.admin")
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public record CmdReload(AdvancedServerList<?> core){
|
||||
|
||||
public static CmdReload create(AdvancedServerList<?> core){
|
||||
return new CmdReload(core);
|
||||
}
|
||||
|
||||
@Command("reload")
|
||||
@CommandDescription("Reloads the plugin's main config and its profiles.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.reload"})
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public void handle(CmdSender sender){
|
||||
sender.sendPrefixedMsg("Reloading plugin...");
|
||||
|
||||
if(core().fileHandler().reloadConfig()){
|
||||
sender.sendPrefixedMsg("<green>Reloaded </green>config.yml<green>!");
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Encountered an error while reloading the </red>config.yml<red>.");
|
||||
sender.sendErrorMsg("<red>See the server console for details.");
|
||||
}
|
||||
|
||||
if(core().fileHandler().reloadProfiles()){
|
||||
sender.sendPrefixedMsg("<green>(Re)loaded <white>%d</white> Profile(s)!", core().fileHandler().profiles().size());
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Encountered errors while (re)loading profiles.");
|
||||
sender.sendErrorMsg("<red>See the server console for details.");
|
||||
}
|
||||
|
||||
sender.sendPrefixedMsg("<green>Reload complete!");
|
||||
}
|
||||
}
|
||||
+24
-609
@@ -29,59 +29,16 @@ import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
|
||||
import ch.andre601.advancedserverlist.core.AdvancedServerList;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
|
||||
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
|
||||
import ch.andre601.advancedserverlist.core.migration.minimotd.MiniMOTDConfigMigrator;
|
||||
import ch.andre601.advancedserverlist.core.migration.serverlistplus.SLPConfigMigrator;
|
||||
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
|
||||
import ch.andre601.advancedserverlist.core.profiles.profile.ProfileSerializer;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.incendo.cloud.CommandManager;
|
||||
import org.incendo.cloud.annotation.specifier.Greedy;
|
||||
import org.incendo.cloud.annotations.Argument;
|
||||
import org.incendo.cloud.annotations.Command;
|
||||
import org.incendo.cloud.annotations.CommandDescription;
|
||||
import org.incendo.cloud.annotations.Permission;
|
||||
import org.incendo.cloud.annotations.suggestion.Suggestions;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
import org.incendo.cloud.context.CommandInput;
|
||||
import org.incendo.cloud.help.result.CommandEntry;
|
||||
import org.incendo.cloud.key.CloudKey;
|
||||
import org.incendo.cloud.minecraft.extras.MinecraftHelp;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
import org.spongepowered.configurate.yaml.NodeStyle;
|
||||
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
@Command("advancedserverlist|asl")
|
||||
@CommandDescription("Main command of the plugin.")
|
||||
@Permission("advancedserverlist.admin")
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public class CommandHandler{
|
||||
|
||||
public static final CloudKey<CommandType.Type> COMMAND_TYPE = CloudKey.of("command_type", CommandType.Type.class);
|
||||
public static final CloudKey<String> OPTION = CloudKey.of("profile_option", String.class);
|
||||
public static final List<String> PROFILE_OPTIONS = List.of(
|
||||
"priority",
|
||||
"condition",
|
||||
"motd",
|
||||
"favicon",
|
||||
"playercount.hideplayers",
|
||||
"playercount.hideplayershover",
|
||||
"playercount.hover",
|
||||
"playercount.text",
|
||||
"playercount.extraplayers.enabled",
|
||||
"playercount.extraplayers.amount",
|
||||
"playercount.maxplayers.enabled",
|
||||
"playercount.maxplayers.amount",
|
||||
"playercount.onlineplayers.enabled",
|
||||
"playercount.onlineplayers.amount"
|
||||
);
|
||||
|
||||
public static void handleSet(AdvancedServerList<?> core, CommandContext<CmdSender> context){
|
||||
CmdSender sender = context.sender();
|
||||
@@ -103,8 +60,8 @@ public class CommandHandler{
|
||||
List<String> oldValues = new ArrayList<>();
|
||||
List<String> newValues = new ArrayList<>();
|
||||
|
||||
switch(option.toLowerCase(Locale.ROOT)){
|
||||
case "priority" -> {
|
||||
switch(CmdProfiles.ProfileOption.fromString(option)){
|
||||
case PRIORITY -> {
|
||||
oldValues.add(String.valueOf(slp.priority()));
|
||||
|
||||
if(value == null || value.isEmpty()){
|
||||
@@ -121,7 +78,7 @@ public class CommandHandler{
|
||||
return;
|
||||
}
|
||||
}
|
||||
case "condition" -> {
|
||||
case CONDITION -> {
|
||||
oldValues.add(slp.condition());
|
||||
|
||||
if(value == null || value.isEmpty()){
|
||||
@@ -132,7 +89,7 @@ public class CommandHandler{
|
||||
condition = value;
|
||||
newValues.add(value);
|
||||
}
|
||||
case "motd" -> {
|
||||
case MOTD -> {
|
||||
oldValues.addAll(entry.motd());
|
||||
|
||||
if(value == null || value.isEmpty()){
|
||||
@@ -140,7 +97,7 @@ public class CommandHandler{
|
||||
break;
|
||||
}
|
||||
|
||||
String[] lines = value.split("\\\\n|<newline>");
|
||||
String[] lines = value.split("\\\\n|<newline>|<br>");
|
||||
if(lines.length == 0){
|
||||
builder.motd(Collections.emptyList());
|
||||
break;
|
||||
@@ -157,7 +114,7 @@ public class CommandHandler{
|
||||
|
||||
newValues.addAll(Arrays.asList(lines));
|
||||
}
|
||||
case "favicon" -> {
|
||||
case FAVICON -> {
|
||||
oldValues.add(entry.favicon());
|
||||
|
||||
if(value == null || value.isEmpty()){
|
||||
@@ -169,7 +126,7 @@ public class CommandHandler{
|
||||
|
||||
newValues.add(value);
|
||||
}
|
||||
case "playercount.hideplayers" -> {
|
||||
case PLAYERCOUNT_HIDEPLAYERS -> {
|
||||
if(!entry.hidePlayersEnabled().isNotSet())
|
||||
oldValues.add(String.valueOf(entry.hidePlayersEnabled().getOrDefault(false)));
|
||||
|
||||
@@ -183,7 +140,7 @@ public class CommandHandler{
|
||||
|
||||
newValues.add(String.valueOf(bool.getOrDefault(false)));
|
||||
}
|
||||
case "playercount.hideplayershover" -> {
|
||||
case PLAYERCOUNT_HIDEPLAYERSHOVER -> {
|
||||
if(!entry.hidePlayersHoverEnabled().isNotSet())
|
||||
oldValues.add(String.valueOf(entry.hidePlayersHoverEnabled().getOrDefault(false)));
|
||||
|
||||
@@ -197,7 +154,7 @@ public class CommandHandler{
|
||||
|
||||
newValues.add(String.valueOf(bool.getOrDefault(false)));
|
||||
}
|
||||
case "playercount.hover" -> {
|
||||
case PLAYERCOUNT_HOVER -> {
|
||||
oldValues.addAll(entry.players());
|
||||
|
||||
if(value == null || value.isEmpty()){
|
||||
@@ -215,7 +172,7 @@ public class CommandHandler{
|
||||
|
||||
newValues.addAll(Arrays.asList(lines));
|
||||
}
|
||||
case "playercount.text" -> {
|
||||
case PLAYERCOUNT_TEXT -> {
|
||||
if(!entry.playerCountText().isEmpty())
|
||||
oldValues.add(entry.playerCountText());
|
||||
|
||||
@@ -228,7 +185,7 @@ public class CommandHandler{
|
||||
|
||||
newValues.add(value);
|
||||
}
|
||||
case "playercount.extraplayers.enabled" -> {
|
||||
case PLAYERCOUNT_EXTRAPLAYERS_ENABLED -> {
|
||||
if(!entry.extraPlayersEnabled().isNotSet())
|
||||
oldValues.add(String.valueOf(entry.extraPlayersEnabled().getOrDefault(false)));
|
||||
|
||||
@@ -242,7 +199,7 @@ public class CommandHandler{
|
||||
|
||||
newValues.add(String.valueOf(bool.getOrDefault(false)));
|
||||
}
|
||||
case "playercount.extraplayers.amount" -> {
|
||||
case PLAYERCOUNT_EXTRAPLAYERS_AMOUNT -> {
|
||||
if(!entry.extraPlayersCount().isEmpty())
|
||||
oldValues.add(entry.extraPlayersCount());
|
||||
|
||||
@@ -255,7 +212,7 @@ public class CommandHandler{
|
||||
|
||||
newValues.add(value);
|
||||
}
|
||||
case "playercount.maxplayers.enabled" -> {
|
||||
case PLAYERCOUNT_MAXPLAYERS_ENABLED -> {
|
||||
if(!entry.maxPlayersEnabled().isNotSet())
|
||||
oldValues.add(String.valueOf(entry.maxPlayersEnabled().getOrDefault(false)));
|
||||
|
||||
@@ -269,7 +226,7 @@ public class CommandHandler{
|
||||
|
||||
newValues.add(String.valueOf(bool.getOrDefault(false)));
|
||||
}
|
||||
case "playercount.maxplayers.amount" -> {
|
||||
case PLAYERCOUNT_MAXPLAYERS_AMOUNT -> {
|
||||
if(!entry.maxPlayersCount().isEmpty())
|
||||
oldValues.add(entry.extraPlayersCount());
|
||||
|
||||
@@ -282,7 +239,7 @@ public class CommandHandler{
|
||||
|
||||
newValues.add(value);
|
||||
}
|
||||
case "playercount.onlineplayers.enabled" -> {
|
||||
case PLAYERCOUNT_ONLINEPLAYERS_ENABLED -> {
|
||||
if(!entry.onlinePlayersEnabled().isNotSet())
|
||||
oldValues.add(String.valueOf(entry.onlinePlayersEnabled().getOrDefault(false)));
|
||||
|
||||
@@ -296,7 +253,7 @@ public class CommandHandler{
|
||||
|
||||
newValues.add(String.valueOf(bool.getOrDefault(false)));
|
||||
}
|
||||
case "playercount.onlineplayers.amount" -> {
|
||||
case PLAYERCOUNT_ONLINEPLAYERS_AMOUNT -> {
|
||||
if(!entry.onlinePlayersCount().isEmpty())
|
||||
oldValues.add(entry.onlinePlayersCount());
|
||||
|
||||
@@ -309,57 +266,13 @@ public class CommandHandler{
|
||||
|
||||
newValues.add(value);
|
||||
}
|
||||
default -> {
|
||||
sender.sendErrorMsg("<red>Unknown option</red> %s<red>!", option);
|
||||
case null, default -> {
|
||||
sender.sendErrorMsg("<red>Unknown option </red>%s<red>!", option);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Path path = core.getPlugin().getFolderPath().resolve("profiles").resolve(profileName(profile));
|
||||
if(!Files.exists(path)){
|
||||
sender.sendErrorMsg("<red>No file with name</red> %s <red>found!", profile);
|
||||
return;
|
||||
}
|
||||
|
||||
YamlConfigurationLoader loader = YamlConfigurationLoader.builder()
|
||||
.path(path)
|
||||
.indent(2)
|
||||
.nodeStyle(NodeStyle.BLOCK)
|
||||
.defaultOptions(opt -> opt.serializers(optBuilder -> optBuilder.register(ProfileEntry.class, ProfileSerializer.INSTANCE)))
|
||||
.build();
|
||||
|
||||
ConfigurationNode node;
|
||||
try{
|
||||
node = loader.load();
|
||||
}catch(IOException ex){
|
||||
sender.sendErrorMsg("<red>Encountered an IOException while trying to load file</red> %s<red>.", profile);
|
||||
sender.sendErrorMsg("<red>Check console for details.");
|
||||
|
||||
core.getPlugin().getPluginLogger().warn("Encountered IOException while loading file <white>%s.yml</white>", profile);
|
||||
return;
|
||||
}
|
||||
|
||||
if(node == null){
|
||||
sender.sendErrorMsg("<red>Unable to load file</red> %s<red>.", profile);
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
node.node("priority").set(priority);
|
||||
node.node("condition").set(condition);
|
||||
node.node("profiles").setList(ProfileEntry.class, slp.profiles());
|
||||
node.set(ProfileEntry.class, builder.build());
|
||||
}catch(SerializationException ex){
|
||||
sender.sendErrorMsg("<red>Encountered a SerializationException while updating</red> %s<red>!", profile);
|
||||
sender.sendErrorMsg("<red>Check console for details.");
|
||||
|
||||
core.getPlugin().getPluginLogger().warn("Encountered SerializationException while updating <white>%s</white>", profile);
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
loader.save(node);
|
||||
|
||||
if(core.fileHandler().saveProfile(profileName(profile), priority, condition, slp.profiles(), builder.build())){
|
||||
if(sender.isPlayer()){
|
||||
sender.sendPrefixedMsg(
|
||||
"<green>Successfully updated <white>[<grey><hover:show_text:\"%s\">%s</hover></grey>]</white>!",
|
||||
@@ -389,22 +302,22 @@ public class CommandHandler{
|
||||
sender.sendPrefixedMsg(
|
||||
"<green>Use <white><click:suggest_command:/asl reload>/asl reload</click></white> to apply changes."
|
||||
);
|
||||
}catch(IOException ex){
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Encountered an IOException while trying to save file</red> %s<red>.", profile);
|
||||
sender.sendErrorMsg("<red>Check console for details.");
|
||||
|
||||
core.getPlugin().getPluginLogger().warn("Encountered IOException while saving file <white>%s.yml</white>", profile);
|
||||
core.plugin().pluginLogger().warn("Encountered IOException while saving file <white>%s.yml</white>", profile);
|
||||
}
|
||||
}
|
||||
|
||||
private static ServerListProfile profile(AdvancedServerList<?> core, String name){
|
||||
return core.getFileHandler().getProfiles().stream()
|
||||
public static ServerListProfile profile(AdvancedServerList<?> core, String name){
|
||||
return core.fileHandler().profiles().stream()
|
||||
.filter(p -> p.file().equalsIgnoreCase(profileName(name)))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private static String profileName(String name){
|
||||
public static String profileName(String name){
|
||||
if(name == null || name.isEmpty())
|
||||
return "";
|
||||
|
||||
@@ -431,503 +344,5 @@ public class CommandHandler{
|
||||
case String str -> str;
|
||||
default -> obj.toString();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private final CommandManager<CmdSender> commandManager;
|
||||
private final MinecraftHelp<CmdSender> help;
|
||||
|
||||
public CommandHandler(CommandManager<CmdSender> commandManager){
|
||||
this.commandManager = commandManager;
|
||||
this.help = MinecraftHelp.<CmdSender>builder()
|
||||
.commandManager(commandManager)
|
||||
.audienceProvider(CmdSender::audience)
|
||||
.commandPrefix("/advancedserverlist help")
|
||||
.colors(MinecraftHelp.helpColors(
|
||||
NamedTextColor.WHITE, NamedTextColor.AQUA, NamedTextColor.WHITE, NamedTextColor.GRAY, NamedTextColor.DARK_GRAY
|
||||
))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Command("help [query]")
|
||||
@CommandDescription("Displays the commands available for AdvancedServerList")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.help"})
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public void help(
|
||||
CmdSender sender,
|
||||
@Nullable @Argument(description = "Query to receive info of a specific command.", suggestions = "help") @Greedy String query
|
||||
){
|
||||
this.help.queryCommands(query == null ? "" : query, sender);
|
||||
}
|
||||
|
||||
@Command("reload")
|
||||
@CommandDescription("Reloads the config.yml and all existing profiles.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.reload"})
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public void reload(CmdSender sender, AdvancedServerList<?> core){
|
||||
sender.sendPrefixedMsg("Reloading plugin...");
|
||||
|
||||
if(core.getFileHandler().reloadConfig()){
|
||||
sender.sendPrefixedMsg("<green>Reloaded </green>config.yml<green>!");
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Error while reloading </red>config.yml<red>.");
|
||||
sender.sendErrorMsg("<red>Check console for details.");
|
||||
}
|
||||
|
||||
if(core.getFileHandler().reloadProfiles()){
|
||||
sender.sendPrefixedMsg("<green>(Re)loaded </green>%d<green> Profile(s)!", core.getFileHandler().getProfiles().size());
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Error while (re)loading profiles.");
|
||||
sender.sendErrorMsg("<red>Check console for details.");
|
||||
}
|
||||
|
||||
sender.sendPrefixedMsg("<green>Reload complete!");
|
||||
}
|
||||
|
||||
@Command("clearcache")
|
||||
@CommandDescription("Clears the Favicon and Player cache of the plugin.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.clearcache"})
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public void clearCache(CmdSender sender, AdvancedServerList<?> core){
|
||||
sender.sendPrefixedMsg("Clearing cache...");
|
||||
|
||||
core.clearFaviconCache();
|
||||
sender.sendPrefixedMsg("Cleared Favicon cache.");
|
||||
|
||||
core.clearPlayerCache();
|
||||
sender.sendPrefixedMsg("Cleared Player cache.");
|
||||
|
||||
sender.sendPrefixedMsg("<green>Cache clearing complete!");
|
||||
}
|
||||
|
||||
@Command("migrate <plugin>")
|
||||
@CommandDescription("Migrates Profiles from other plugins to itself.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.migrate"})
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public void migrate(
|
||||
CmdSender sender,
|
||||
AdvancedServerList<?> core,
|
||||
@Nonnull @Argument(description = "The plugin to migrate from.") Plugin plugin
|
||||
){
|
||||
if(plugin == Plugin.SERVERLISTPLUS){
|
||||
if(!core.getPlugin().isPluginEnabled("ServerListPlus")){
|
||||
sender.sendErrorMsg("<red>ServerListPlus is not enabled.");
|
||||
sender.sendErrorMsg("<red>The plugin is required for the migration to work.");
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendPrefixedMsg("Migrating from ServerListPlus...");
|
||||
|
||||
int migrated = SLPConfigMigrator.migrate(core, sender);
|
||||
if(migrated == 0){
|
||||
sender.sendErrorMsg("<red>Unable to migrate any profiles from ServerListPlus.");
|
||||
sender.sendErrorMsg("<red>Check console for details.");
|
||||
}else{
|
||||
sender.sendPrefixedMsg("<green>Successfully migrated </green>%d<green> Profile(s)", migrated);
|
||||
}
|
||||
}else
|
||||
if(plugin == Plugin.MINIMOTD){
|
||||
sender.sendPrefixedMsg("Migrating from MiniMOTD...");
|
||||
|
||||
if(MiniMOTDConfigMigrator.migrate(core, sender)){
|
||||
sender.sendPrefixedMsg("<green>Successfully migrated from MiniMOTD!");
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Unable to migrate from MiniMOTD.");
|
||||
sender.sendErrorMsg("<red>Check console for details.");
|
||||
}
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Received invalid Plugin name </red>%s<red>.", plugin);
|
||||
sender.sendMsg("<grey>Supported plugins [Click for command]:");
|
||||
sender.sendMsg();
|
||||
sender.sendMsg(" <grey>-</grey> <click:suggest_command:/asl migrate serverlistplus>ServerListPlus</click>");
|
||||
sender.sendMsg(" <grey>-</grey> <click:suggest_command:/asl migrate minimotd>MiniMOTD</click>");
|
||||
}
|
||||
}
|
||||
|
||||
@Command("profiles list")
|
||||
@CommandDescription("Lists all available profiles with their priority, conditions and if they are valid.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.profiles"})
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public void list(CmdSender sender, AdvancedServerList<?> core){
|
||||
sender.sendMsg();
|
||||
sender.sendPrefixedMsg("Available Profiles");
|
||||
sender.sendMsg("<dark_grey>│");
|
||||
|
||||
List<ServerListProfile> profiles = core.getFileHandler().getProfiles().stream()
|
||||
.sorted(Comparator.comparingInt(ServerListProfile::priority).reversed())
|
||||
.toList();
|
||||
|
||||
if(profiles.isEmpty()){
|
||||
sender.sendMsg("<dark_grey>└─</dark_grey> <i>No profiles found</i>");
|
||||
}else{
|
||||
for(int i = 0; i < profiles.size(); i++){
|
||||
sender.sendMsg(
|
||||
"<dark_grey>%s─</dark_grey> <aqua>" +
|
||||
"<hover:show_text:\"%s\">" +
|
||||
"<click:suggest_command:/asl profiles info %s>" +
|
||||
"%s" +
|
||||
"</click>" +
|
||||
"</hover>" +
|
||||
"</aqua>",
|
||||
(i + 1) == profiles.size() ? "└" : "├",
|
||||
hover(profiles.get(i)),
|
||||
profiles.get(i).file(),
|
||||
profiles.get(i).file()
|
||||
);
|
||||
}
|
||||
}
|
||||
if(sender.isPlayer()){
|
||||
sender.sendMsg();
|
||||
sender.sendMsg("<grey>[<white>Hover a name for details. Click for a command</white>]</grey>");
|
||||
}
|
||||
sender.sendMsg();
|
||||
}
|
||||
|
||||
@Command("profiles info <profile>")
|
||||
@CommandDescription("Provides info about a profile.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.profiles"})
|
||||
@CommandType(CommandType.Type.PLAYER_ONLY)
|
||||
public void info(
|
||||
CmdSender sender,
|
||||
AdvancedServerList<?> core,
|
||||
@Nonnull @Argument(description = "Name of the profile to view.", suggestions = "profiles") String profile
|
||||
){
|
||||
if(!sender.isPlayer()){
|
||||
sender.sendErrorMsg("<red>This command can only be executed by Players!");
|
||||
return;
|
||||
}
|
||||
|
||||
if(profile.isEmpty()){
|
||||
sender.sendErrorMsg("<red>Insufficient arguments.");
|
||||
sender.sendErrorMsg("<red>Please provide a proper profile name.");
|
||||
return;
|
||||
}
|
||||
|
||||
ServerListProfile slp = profile(core, profile);
|
||||
|
||||
if(slp == null){
|
||||
sender.sendErrorMsg("<red>Unable to find profile with name </red>%s<red>.", profile);
|
||||
sender.sendErrorMsg("<red>Make sure you typed it correctly.");
|
||||
return;
|
||||
}
|
||||
|
||||
ProfileOptionsSender optionsSender = new ProfileOptionsSender(slp.file());
|
||||
|
||||
optionsSender
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Priority", slp.priority())
|
||||
.option("priority")
|
||||
)
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Condition", slp.condition())
|
||||
.option("condition")
|
||||
)
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Profiles")
|
||||
.color(slp.profiles().isEmpty() ? ProfileOptionsSender.ColorName.RED : ProfileOptionsSender.ColorName.GREEN)
|
||||
);
|
||||
|
||||
if(!slp.profiles().isEmpty()){
|
||||
for(int i = 0; i < slp.profiles().size(); i++){
|
||||
optionsSender.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("#" + (i + 1), profileHover(slp.profiles().get(i)))
|
||||
.prefix("│ %s─".formatted((i + 1) == slp.profiles().size() ? "└" : "├"))
|
||||
.color(ProfileOptionsSender.ColorName.AQUA)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ProfileEntry entry = slp.defaultProfile();
|
||||
|
||||
optionsSender
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("MOTD", entry.motd())
|
||||
.option("motd")
|
||||
)
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Favicon", entry.favicon())
|
||||
.option("favicon")
|
||||
)
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Player count")
|
||||
.prefix("└─")
|
||||
.color(ProfileOptionsSender.ColorName.AQUA)
|
||||
)
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Hide Player count?", entry.hidePlayersEnabled())
|
||||
.prefix(" ├─")
|
||||
.option("playercount.hideplayers")
|
||||
)
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Hide Player count hover?", entry.hidePlayersHoverEnabled())
|
||||
.prefix(" ├─")
|
||||
.option("playercount.hideplayershover")
|
||||
)
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Player count hover", entry.players())
|
||||
.prefix(" ├─")
|
||||
.option("playercount.hover")
|
||||
)
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Player count text", entry.playerCountText())
|
||||
.prefix(" ├─")
|
||||
.option("playercount.text")
|
||||
)
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Extra Players")
|
||||
.prefix(" ├─")
|
||||
.color(ProfileOptionsSender.ColorName.AQUA)
|
||||
)
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Enabled?", entry.extraPlayersEnabled())
|
||||
.prefix(" │ ├─")
|
||||
.option("playercount.extraplayers.enabled")
|
||||
)
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Amount", entry.extraPlayersCount())
|
||||
.prefix(" │ └─")
|
||||
.option("playercount.extraplayers.amount")
|
||||
)
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Max Players")
|
||||
.prefix(" ├─")
|
||||
.color(ProfileOptionsSender.ColorName.AQUA)
|
||||
)
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Enabled?", entry.maxPlayersEnabled())
|
||||
.prefix(" │ ├─")
|
||||
.option("playercount.maxplayers.enabled")
|
||||
)
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Amount", entry.maxPlayersCount())
|
||||
.prefix(" │ └─")
|
||||
.option("playercount.maxplayers.amount")
|
||||
)
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Online Players")
|
||||
.prefix(" └─")
|
||||
.color(ProfileOptionsSender.ColorName.AQUA)
|
||||
)
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Enabled?", entry.onlinePlayersEnabled())
|
||||
.prefix(" ├─")
|
||||
.option("playercount.onlineplayers.enabled")
|
||||
)
|
||||
.append(
|
||||
ProfileOptionsSender.OptionStringBuilder.of("Amount", entry.onlinePlayersCount())
|
||||
.prefix(" └─")
|
||||
.option("playercount.onlineplayers.amount")
|
||||
)
|
||||
.send(sender);
|
||||
}
|
||||
|
||||
@Command("profiles add <name>")
|
||||
@CommandDescription("Creates a new profile with default values applied.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.profiles"})
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public void add(
|
||||
CmdSender sender,
|
||||
AdvancedServerList<?> core,
|
||||
@Nonnull @Argument(description = "Name of the profile to create.") String name
|
||||
){
|
||||
if(name.isEmpty()){
|
||||
sender.sendErrorMsg("<red>Insufficient arguments.");
|
||||
sender.sendErrorMsg("<red>Please provide a proper file name.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(profile(core, name) != null){
|
||||
sender.sendErrorMsg("<red>A profile with name </red>%s<red> already exists.", name);
|
||||
return;
|
||||
}
|
||||
|
||||
String fileName = profileName(name);
|
||||
|
||||
if(core.getFileHandler().createFile(fileName)){
|
||||
sender.sendPrefixedMsg("<green>Successfully created </green>%s<green>!", fileName);
|
||||
sender.sendPrefixedMsg("<green>Use <white><click:suggest_command:/asl reload>/asl reload</click></white> to load the file.");
|
||||
}else{
|
||||
sender.sendErrorMsg("<red>Error while creating new profile.");
|
||||
sender.sendErrorMsg("<red>Check console for details.");
|
||||
}
|
||||
}
|
||||
|
||||
@Command("profiles copy <profile> <name>")
|
||||
@CommandDescription("Creates a copy of an existing profile.")
|
||||
@Permission({"advancedserverlist.admin", "advancedserverlist.command.profiles"})
|
||||
@CommandType(CommandType.Type.ALL)
|
||||
public void copy(
|
||||
CmdSender sender,
|
||||
AdvancedServerList<?> core,
|
||||
@Nonnull @Argument(description = "Name of the profile to copy.", suggestions = "profiles") String profile,
|
||||
@Nonnull @Argument(description = "Name of the copy.") String name
|
||||
){
|
||||
if(profile.isEmpty() || name.isEmpty()){
|
||||
sender.sendErrorMsg("<red>Insufficient arguments.");
|
||||
sender.sendErrorMsg("<red>Please provide a profile name and name for the copy.");
|
||||
return;
|
||||
}
|
||||
|
||||
String profileName = profileName(profile);
|
||||
String copyName = profileName(name);
|
||||
|
||||
ServerListProfile targetProfile = profile(core, profile);
|
||||
boolean copyExists = profile(core, name) != null;
|
||||
|
||||
if(targetProfile == null){
|
||||
sender.sendErrorMsg("<red>No profile with name </red>%s<red> exists.", profile);
|
||||
return;
|
||||
}
|
||||
|
||||
if(copyExists){
|
||||
sender.sendErrorMsg("<red>A file with name </red>%<red> already exists!", copyName);
|
||||
return;
|
||||
}
|
||||
|
||||
Path profilePath = core.getPlugin().getFolderPath().resolve("profiles").resolve(copyName);
|
||||
if(!Files.exists(profilePath)){
|
||||
try{
|
||||
Files.createFile(profilePath);
|
||||
}catch(IOException ex){
|
||||
sender.sendErrorMsg("<red>Encountered IOException while creating file </red>%s<red>.", copyName);
|
||||
sender.sendErrorMsg("<red>Check console for details.");
|
||||
|
||||
core.getPlugin().getPluginLogger().warn("Encountered IOException while creating file <white>%s</white>", ex, copyName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
YamlConfigurationLoader loader = YamlConfigurationLoader.builder()
|
||||
.path(profilePath)
|
||||
.indent(2)
|
||||
.nodeStyle(NodeStyle.BLOCK)
|
||||
.defaultOptions(opt -> opt.serializers(builder -> builder.register(ProfileEntry.class, ProfileSerializer.INSTANCE)))
|
||||
.build();
|
||||
|
||||
ConfigurationNode node;
|
||||
try{
|
||||
node = loader.load();
|
||||
}catch(IOException ex){
|
||||
sender.sendErrorMsg("<red>There was an IOException while loading file </red>%s<red>!", copyName);
|
||||
sender.sendErrorMsg("<red>Check console for details.");
|
||||
|
||||
core.getPlugin().getPluginLogger().warn("Encountered IOException while loading file <white>%s</white>", ex, copyName);
|
||||
return;
|
||||
}
|
||||
|
||||
if(node == null){
|
||||
sender.sendErrorMsg("<red>Cannot create ConfigurationNode for file </red>%s<red>", copyName);
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
node.node("priority").set(targetProfile.priority());
|
||||
node.node("condition").set(targetProfile.condition());
|
||||
|
||||
node.node("profiles").setList(ProfileEntry.class, targetProfile.profiles());
|
||||
|
||||
node.set(ProfileEntry.class, targetProfile.defaultProfile());
|
||||
}catch(SerializationException ex){
|
||||
sender.sendErrorMsg("<red>There was a SerializationException while copying values from </red>%s<red>.", profile);
|
||||
sender.sendErrorMsg("<red>Check console for details.");
|
||||
|
||||
core.getPlugin().getPluginLogger().warn("Encountered IOException while copying data from <white>%s</white> to <white>%s</white>.", ex, profileName, copyName);
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
loader.save(node);
|
||||
sender.sendPrefixedMsg("<green>Successfully made copy of </green>%s<green> as </green>%s<green>!", profile, copyName);
|
||||
sender.sendPrefixedMsg("<green>Use </green><click:suggest_command:/asl reload>/asl reload</click><green> to load the file.");
|
||||
}catch(IOException ex){
|
||||
sender.sendErrorMsg("<red>There was an IOException while loading file </red>%s<red>!", copyName);
|
||||
sender.sendErrorMsg("<red>Check console for details.");
|
||||
|
||||
core.getPlugin().getPluginLogger().warn("Encountered IOException while loading file <white>%s</white>", ex, copyName);
|
||||
}
|
||||
}
|
||||
|
||||
@Suggestions("help")
|
||||
public List<String> helpQueries(CommandContext<CmdSender> context){
|
||||
return this.commandManager.createHelpHandler()
|
||||
.queryRootIndex(context.sender())
|
||||
.entries()
|
||||
.stream()
|
||||
.filter(entry -> context.hasPermission(entry.command().commandPermission()))
|
||||
.filter(entry -> {
|
||||
CommandType.Type type = entry.command().commandMeta().getOrDefault(COMMAND_TYPE, CommandType.Type.ALL);
|
||||
|
||||
return type == CommandType.Type.ALL || type == CommandType.Type.PLAYER_ONLY && context.sender().isPlayer();
|
||||
})
|
||||
.map(CommandEntry::syntax)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Suggestions("profiles")
|
||||
public List<String> profilesSuggestions(CommandInput input, AdvancedServerList<?> core){
|
||||
String name = input.readString();
|
||||
List<String> profiles = core.getFileHandler().getProfiles().stream()
|
||||
.map(profile -> profile.file().substring(0, profile.file().lastIndexOf('.')))
|
||||
.toList();
|
||||
if(name.isEmpty())
|
||||
return profiles;
|
||||
|
||||
List<String> matches = new ArrayList<>(profiles.size());
|
||||
for(String str : profiles){
|
||||
if(str.length() >= name.length() && str.regionMatches(true, 0, name, 0, name.length()))
|
||||
matches.add(str);
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
private String hover(ServerListProfile profile){
|
||||
return """
|
||||
<grey>Priority: <white>%d</white>
|
||||
Condition: <white>%s</white>
|
||||
|
||||
Is valid? %s""".formatted(
|
||||
profile.priority(),
|
||||
profile.condition() == null || profile.condition().isEmpty() ? "<i>None</i>" : profile.condition(),
|
||||
profile.isInvalidProfile() ? "<red>No</red>" : "<green>Yes</green>"
|
||||
);
|
||||
}
|
||||
|
||||
private String profileHover(ProfileEntry entry){
|
||||
return """
|
||||
<grey>MOTD:
|
||||
%s<reset>
|
||||
|
||||
<grey>Favicon: %s<reset>
|
||||
|
||||
<grey>Hide Playercount? %s
|
||||
Hide Playercount Hover? %s
|
||||
|
||||
Playercount Hover:
|
||||
%s<reset>
|
||||
|
||||
<grey>Playercount Text: %s<reset>
|
||||
|
||||
<grey>Extra Players: %s<reset>
|
||||
<grey>Max Players: %s<reset>
|
||||
<grey>Online Players: %s""".formatted(
|
||||
entry.motd() == null || entry.motd().isEmpty() ?
|
||||
"<i><white>None</white></i>" : String.join("\n", entry.motd()),
|
||||
entry.favicon() == null || entry.favicon().isEmpty() ?
|
||||
"<i><white>None</white></i>" : entry.favicon(),
|
||||
entry.hidePlayersEnabled().getOrDefault(false) ? "<green>Enabled</green>" : "<red>Disabled</red>",
|
||||
entry.hidePlayersHoverEnabled().getOrDefault(false) ? "<green>Enabled</green>" : "<red>Disabled</red>",
|
||||
entry.players().isEmpty() ? "<i><white>None</white></i>" : String.join("\n", entry.players()),
|
||||
entry.playerCountText() == null || entry.playerCountText().isEmpty() ?
|
||||
"<i><white>None</white></i>" : entry.playerCountText(),
|
||||
entry.extraPlayersEnabled().getOrDefault(false) ? entry.extraPlayersCount() : "<red>Disabled</red>",
|
||||
entry.maxPlayersEnabled().getOrDefault(false) ? entry.maxPlayersCount() : "<red>Disabled</red>",
|
||||
entry.onlinePlayersEnabled().getOrDefault(false) ? entry.onlinePlayersCount() : "<red>Disabled</red>"
|
||||
);
|
||||
}
|
||||
|
||||
public enum Plugin {
|
||||
SERVERLISTPLUS,
|
||||
MINIMOTD
|
||||
}
|
||||
}
|
||||
+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();
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -90,7 +90,7 @@ public class ProfileOptionsSender{
|
||||
private String prefix = "├─";
|
||||
private ColorName color = ColorName.AQUA;
|
||||
private String hover = "<i>Not set</i>";
|
||||
private String option = null;
|
||||
private CmdProfiles.ProfileOption option = null;
|
||||
|
||||
private OptionStringBuilder(String displayName){
|
||||
this.displayName = displayName;
|
||||
@@ -116,7 +116,7 @@ public class ProfileOptionsSender{
|
||||
return this;
|
||||
}
|
||||
|
||||
public OptionStringBuilder option(String option){
|
||||
public OptionStringBuilder option(CmdProfiles.ProfileOption option){
|
||||
this.option = option;
|
||||
return this;
|
||||
}
|
||||
@@ -135,7 +135,7 @@ public class ProfileOptionsSender{
|
||||
.append("\">");
|
||||
}
|
||||
|
||||
if(option != null && !option.isEmpty() && file != null && !file.isEmpty()){
|
||||
if(option != null && file != null && !file.isEmpty()){
|
||||
builder.append("<click:suggest_command:/asl profiles set ")
|
||||
.append(file)
|
||||
.append(" ")
|
||||
@@ -145,7 +145,7 @@ public class ProfileOptionsSender{
|
||||
|
||||
builder.append(displayName);
|
||||
|
||||
if(option != null && !option.isEmpty())
|
||||
if(option != null && file != null && !file.isEmpty())
|
||||
builder.append("</click>");
|
||||
|
||||
if(hover != null && !hover.isEmpty())
|
||||
@@ -159,7 +159,7 @@ public class ProfileOptionsSender{
|
||||
}
|
||||
}
|
||||
|
||||
enum ColorName{
|
||||
public enum ColorName{
|
||||
RED,
|
||||
GREEN,
|
||||
AQUA;
|
||||
|
||||
+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){}
|
||||
}
|
||||
+33
-25
@@ -44,28 +44,28 @@ 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...");
|
||||
@@ -93,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!");
|
||||
}
|
||||
@@ -110,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!");
|
||||
}
|
||||
@@ -127,19 +129,20 @@ 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("', '", entry.motd()));
|
||||
@@ -148,12 +151,13 @@ public class PingEventHandler{
|
||||
.map(line -> ComponentParser.text(line)
|
||||
.modifyText(text -> StringReplacer.replace(text, player, finalServer))
|
||||
.modifyText(text -> event.parsePAPIPlaceholders(text, player))
|
||||
.modifyText(text -> plugin.getCore().getTextCenterUtil().getCenteredText(text))
|
||||
.modifyText(text -> plugin.core().textCenterUtil().getCenteredText(text))
|
||||
.applyMMTags(event.plugin())
|
||||
.toComponent()
|
||||
)
|
||||
.toList();
|
||||
|
||||
event.setMotd(Component.join(JoinConfiguration.separator(Component.newline()), motd));
|
||||
event.motd(Component.join(JoinConfiguration.separator(Component.newline()), motd));
|
||||
}
|
||||
|
||||
boolean hidePlayers = ProfileManager.checkOption(entry.hidePlayersEnabled());
|
||||
@@ -167,10 +171,11 @@ 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()
|
||||
);
|
||||
}
|
||||
@@ -178,30 +183,33 @@ public class PingEventHandler{
|
||||
boolean hidePlayersHover = ProfileManager.checkOption(entry.hidePlayersHoverEnabled());
|
||||
if(hidePlayersHover){
|
||||
logger.debug(PingEventHandler.class, "'playerCount -> hidePlayersHover' option set. Hiding Player Hover...");
|
||||
event.setPlayersHidden();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,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,9 @@ public class ConfigMigrator{
|
||||
public static ConfigurationTransformation.Versioned create(){
|
||||
return ConfigurationTransformation.versionedBuilder()
|
||||
.versionKey("configVersion")
|
||||
.addVersion(LATEST, sevenToEight())
|
||||
.addVersion(LATEST, nineToTen())
|
||||
.addVersion(9, eightToNine())
|
||||
.addVersion(8, sevenToEight())
|
||||
.addVersion(7, sixToSeven())
|
||||
.addVersion(6, fiveToSix())
|
||||
.addVersion(5, fourToFive())
|
||||
@@ -56,6 +58,22 @@ 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) -> {
|
||||
|
||||
@@ -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,18 +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(){
|
||||
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;
|
||||
@@ -106,6 +117,48 @@ 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.debugSuccess(FileHandler.class, "Successfully created profiles folder.");
|
||||
@@ -219,7 +272,7 @@ public class FileHandler{
|
||||
private boolean makeBackup(){
|
||||
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.failure("Cannot create backups folder for migration!");
|
||||
return false;
|
||||
|
||||
+18
-9
@@ -26,8 +26,11 @@
|
||||
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;
|
||||
|
||||
@@ -48,28 +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();
|
||||
|
||||
CommandManager<CmdSender> getCommandManager();
|
||||
CommandManager<CmdSender> commandManager();
|
||||
|
||||
String getPlatformInfo();
|
||||
String platformInfo();
|
||||
|
||||
String getLoader();
|
||||
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");
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package ch.andre601.advancedserverlist.core.interfaces.dialogs;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.NullBool;
|
||||
import ch.andre601.advancedserverlist.core.commands.CmdProfiles;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IDialog<T>{
|
||||
T createDialog();
|
||||
|
||||
interface IDialogBuilder{
|
||||
IDialogBuilder appendBody(String body);
|
||||
|
||||
IDialogBuilder appendTextInput(CmdProfiles.ProfileOption option, String title, String initValue, int maxLength);
|
||||
|
||||
IDialogBuilder appendTextbox(CmdProfiles.ProfileOption option, String title, List<String> initValue, int maxLength, int rows);
|
||||
|
||||
IDialogBuilder appendNumberRange(CmdProfiles.ProfileOption option, String title, int initValue);
|
||||
|
||||
IDialogBuilder appendBoolean(CmdProfiles.ProfileOption option, String title, NullBool initValue);
|
||||
|
||||
IDialog<?> build();
|
||||
}
|
||||
}
|
||||
+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();
|
||||
}
|
||||
}
|
||||
+15
-15
@@ -40,23 +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 setPlayersHidden();
|
||||
void playersHidden();
|
||||
|
||||
void setFavicon(F favicon);
|
||||
void favicon(F favicon);
|
||||
|
||||
void setDefaultFavicon();
|
||||
void defaultFavicon();
|
||||
|
||||
void updateEvent();
|
||||
|
||||
@@ -64,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();
|
||||
|
||||
+9
-7
@@ -49,12 +49,14 @@ 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("[<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.");
|
||||
@@ -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,7 +111,7 @@ public class MiniMOTDConfigMigrator{
|
||||
|
||||
if(config.iconEnabled()){
|
||||
if(config.motds().size() == 1){
|
||||
MiniMOTDConfig.Motd motd = config.motds().get(0);
|
||||
MiniMOTDConfig.Motd motd = config.motds().getFirst();
|
||||
if(!motd.icon().isEmpty()){
|
||||
profileBuilder.favicon(motd.icon());
|
||||
}
|
||||
@@ -152,7 +154,7 @@ public class MiniMOTDConfigMigrator{
|
||||
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("[<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.");
|
||||
|
||||
+4
-4
@@ -99,7 +99,7 @@ public class SLPConfigMigrator{
|
||||
|
||||
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("[<white>Migrator - ServerListPlus</white>] Cannot migrate ServerListPlus config. ServerListPlus is not active!");
|
||||
return 0;
|
||||
@@ -128,7 +128,7 @@ 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("[<white>Migrator - ServerListPlus</white>] No StatusConf found for type <white>%s</white>. Skipping...", type.getName());
|
||||
sender.sendPrefixedMsg(" -> Not found! Skipping...");
|
||||
@@ -176,7 +176,7 @@ public class SLPConfigMigrator{
|
||||
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("[<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());
|
||||
@@ -223,7 +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")
|
||||
|
||||
@@ -25,8 +25,11 @@
|
||||
|
||||
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.function.Function;
|
||||
@@ -61,6 +64,17 @@ public class ComponentParser{
|
||||
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;
|
||||
}
|
||||
|
||||
public Component toComponent(){
|
||||
return mm.deserialize(text);
|
||||
}
|
||||
|
||||
@@ -77,8 +77,8 @@ public class TextCenterUtil{
|
||||
private Map<String, FontInfo> loadFontWidths(AdvancedServerList<?> plugin){
|
||||
try(InputStream stream = plugin.getClass().getResourceAsStream("/char-widths.json.gz")){
|
||||
if(stream == null){
|
||||
plugin.getPlugin().getPluginLogger().warn("Internal char-widths.json.gz couldn't be retrieved.");
|
||||
plugin.getPlugin().getPluginLogger().warn("The <center> Placeholder won't work.");
|
||||
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<>();
|
||||
}
|
||||
|
||||
@@ -87,9 +87,9 @@ public class TextCenterUtil{
|
||||
|
||||
return new Gson().fromJson(reader, new TypeToken<Map<String, FontInfo>>(){}.getType());
|
||||
}catch(IOException ex){
|
||||
plugin.getPlugin().getPluginLogger().warn("Encountered IOException while loading font widths.");
|
||||
plugin.getPlugin().getPluginLogger().warn("The <center> Placeholder won't work.");
|
||||
plugin.getPlugin().getPluginLogger().warn("Cause: <white>%s</white>", ex.getMessage());
|
||||
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<>();
|
||||
}
|
||||
}
|
||||
|
||||
+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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-3
@@ -46,9 +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;
|
||||
try{
|
||||
result = core.parser().compileToBoolean(condition, collector, player, server);
|
||||
}catch(Exception ex){
|
||||
logger.warn("Encountered an Exception while parsing condition '%s'. Cause: %s", condition, ex.getMessage());
|
||||
if(core.fileHandler().getBool(false, "debug"))
|
||||
logger.warn("Stacktrace:", ex);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if(collector.hasWarnings()){
|
||||
logger.warn("Encountered <white>%d</white> Error(s) while parsing condition for file '<white>%s</white>':", collector.getWarnings().size(), file);
|
||||
@@ -70,7 +79,7 @@ public record ServerListProfile(int priority, String condition, ProfileEntry def
|
||||
}
|
||||
|
||||
if(profiles.size() == 1)
|
||||
return profiles.get(0); // No need to run a random for 1 profile.
|
||||
return profiles.getFirst(); // No need to run a random for 1 profile.
|
||||
|
||||
synchronized(random){
|
||||
return profiles.get(random.nextInt(profiles.size()));
|
||||
|
||||
-82
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.profiles.conditions;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.reader.PlaceholderTokenReader;
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.reader.PlaceholderValueReader;
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.tokens.CustomExpressionTokenizer;
|
||||
import ch.andre601.expressionparser.ExpressionParserEngine;
|
||||
import ch.andre601.expressionparser.ParseWarnCollector;
|
||||
import ch.andre601.expressionparser.operator.Operator;
|
||||
import ch.andre601.expressionparser.parsers.ExpressionTemplateParser;
|
||||
import ch.andre601.expressionparser.parsers.ValueReader;
|
||||
import ch.andre601.expressionparser.templates.ExpressionTemplate;
|
||||
import ch.andre601.expressionparser.tokens.Token;
|
||||
import ch.andre601.expressionparser.tokens.readers.TokenReader;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ProfileConditionParser implements ExpressionParserEngine{
|
||||
|
||||
private final CustomExpressionTokenizer tokenizer;
|
||||
private final ExpressionTemplateParser parser;
|
||||
|
||||
private ProfileConditionParser(List<TokenReader> tokenReaders, ImmutableMap<Token, Operator> operators, ImmutableList<ValueReader> valueReaders){
|
||||
this.tokenizer = new CustomExpressionTokenizer(tokenReaders);
|
||||
this.parser = new ExpressionTemplateParser(operators, valueReaders);
|
||||
}
|
||||
|
||||
public static ProfileConditionParser create(){
|
||||
return new ProfileConditionParserBuilder().addDefaults()
|
||||
.addTokenReader(new PlaceholderTokenReader(-20))
|
||||
.addValueReader(new PlaceholderValueReader())
|
||||
.build();
|
||||
}
|
||||
|
||||
public boolean evaluate(String text, GenericPlayer player, GenericServer server, ParseWarnCollector collector){
|
||||
ExpressionTemplate result = parser.parse(tokenizer.parse(text, player, server, collector), collector);
|
||||
if(result == null)
|
||||
return false;
|
||||
|
||||
return result.returnBooleanExpression().evaluate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionTemplate compile(String s, ParseWarnCollector parseWarnCollector){
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class ProfileConditionParserBuilder extends ExpressionParserEngine.Builder<ProfileConditionParser>{
|
||||
@Override
|
||||
public ProfileConditionParser build(){
|
||||
return new ProfileConditionParser(this.getTokenReaders(), ImmutableMap.copyOf(this.getOperators()), ImmutableList.copyOf(this.getValueReaders()));
|
||||
}
|
||||
}
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.reader;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.expressionparser.ParseWarnCollector;
|
||||
import ch.andre601.expressionparser.tokens.Token;
|
||||
import ch.andre601.expressionparser.tokens.readers.TokenReader;
|
||||
|
||||
import java.text.ParsePosition;
|
||||
|
||||
public abstract class CustomTokenReader extends TokenReader{
|
||||
|
||||
public CustomTokenReader(int priority){
|
||||
super(priority);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token read(String text, ParsePosition position, ParseWarnCollector collector){
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract Token read(String text, ParsePosition position, GenericPlayer player, GenericServer server, ParseWarnCollector collector);
|
||||
}
|
||||
-92
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2022-2024 Andre_601
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.tokens;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.reader.CustomTokenReader;
|
||||
import ch.andre601.expressionparser.ParseWarnCollector;
|
||||
import ch.andre601.expressionparser.tokens.Token;
|
||||
import ch.andre601.expressionparser.tokens.readers.TokenReader;
|
||||
import com.google.common.collect.Ordering;
|
||||
|
||||
import java.text.ParsePosition;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class CustomExpressionTokenizer{
|
||||
|
||||
private static final Ordering<TokenReader> TOKEN_READER_ORDERING = Ordering.from(Comparator.comparingInt(TokenReader::getPriority)).reverse();
|
||||
|
||||
private final List<TokenReader> tokenReaders;
|
||||
|
||||
public CustomExpressionTokenizer(Iterable<TokenReader> tokenReaders){
|
||||
this.tokenReaders = TOKEN_READER_ORDERING.immutableSortedCopy(tokenReaders);
|
||||
}
|
||||
|
||||
public List<Token> parse(String text, GenericPlayer player, GenericServer server, ParseWarnCollector collector){
|
||||
ParsePosition position = new ParsePosition(0);
|
||||
|
||||
List<Token> tokens = new LinkedList<>();
|
||||
|
||||
next_token:
|
||||
while(true){
|
||||
while(position.getIndex() < text.length() && Character.isWhitespace(text.charAt(position.getIndex())))
|
||||
position.setIndex(position.getIndex() + 1);
|
||||
|
||||
if(position.getIndex() >= text.length())
|
||||
break;
|
||||
|
||||
for(TokenReader tokenReader : tokenReaders){
|
||||
Token token;
|
||||
if(tokenReader instanceof CustomTokenReader customTokenReader){
|
||||
if(null != (token = customTokenReader.read(text, position, player, server, collector))){
|
||||
tokens.add(token);
|
||||
continue next_token;
|
||||
}
|
||||
}else{
|
||||
if(null != (token = tokenReader.read(text, position, collector))){
|
||||
tokens.add(token);
|
||||
continue next_token;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
}
|
||||
+25
-6
@@ -23,27 +23,46 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.reader;
|
||||
package ch.andre601.advancedserverlist.core.profiles.conditions.reader;
|
||||
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.tokens.PlaceholderToken;
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.PlaceholderToken;
|
||||
import ch.andre601.advancedserverlist.core.profiles.replacer.StringReplacer;
|
||||
import ch.andre601.expressionparser.ParseWarnCollector;
|
||||
import ch.andre601.expressionparser.tokens.Token;
|
||||
import ch.andre601.expressionparser.tokens.readers.TokenReader;
|
||||
|
||||
import java.text.ParsePosition;
|
||||
|
||||
public class PlaceholderTokenReader extends CustomTokenReader{
|
||||
public class PlaceholderTokenReader extends TokenReader{
|
||||
|
||||
public PlaceholderTokenReader(int priority){
|
||||
super(priority);
|
||||
public PlaceholderTokenReader(){
|
||||
super(-20);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token read(String text, ParsePosition position, GenericPlayer player, GenericServer server, ParseWarnCollector collector){
|
||||
public Token read(String text, ParsePosition position, ParseWarnCollector collector, Object... args){
|
||||
GenericPlayer player = null;
|
||||
GenericServer server = null;
|
||||
|
||||
for(Object arg : args){
|
||||
if(arg instanceof GenericPlayer pl){
|
||||
player = pl;
|
||||
}else
|
||||
if(arg instanceof GenericServer srv){
|
||||
server = srv;
|
||||
}
|
||||
}
|
||||
|
||||
if(player == null || server == null){
|
||||
collector.appendWarning(position.getIndex(), "Received null GenericPlayer or GenericServer instance.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if(position.getIndex() + 1 < text.length() && text.charAt(position.getIndex()) == '$' && text.charAt(position.getIndex() + 1) == '{'){
|
||||
position.setIndex(position.getIndex() + 2);
|
||||
|
||||
return new PlaceholderToken(StringReplacer.parsePlaceholder(text, position, player, server, collector));
|
||||
}
|
||||
return null;
|
||||
+4
-4
@@ -23,9 +23,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.reader;
|
||||
package ch.andre601.advancedserverlist.core.profiles.conditions.reader;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.tokens.PlaceholderToken;
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.PlaceholderToken;
|
||||
import ch.andre601.expressionparser.ParseWarnCollector;
|
||||
import ch.andre601.expressionparser.parsers.ExpressionTemplateParser;
|
||||
import ch.andre601.expressionparser.parsers.ValueReader;
|
||||
@@ -37,8 +37,8 @@ import java.util.List;
|
||||
public class PlaceholderValueReader extends ValueReader{
|
||||
@Override
|
||||
public ExpressionTemplate read(ExpressionTemplateParser parser, List<Token> tokens, ParseWarnCollector collector){
|
||||
if(tokens.get(0) instanceof PlaceholderToken){
|
||||
PlaceholderToken token = (PlaceholderToken)tokens.remove(0);
|
||||
if(tokens.getFirst() instanceof PlaceholderToken){
|
||||
PlaceholderToken token = (PlaceholderToken)tokens.removeFirst();
|
||||
return token.getValue();
|
||||
}
|
||||
return null;
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.tokens;
|
||||
package ch.andre601.advancedserverlist.core.profiles.conditions.tokens;
|
||||
|
||||
import ch.andre601.expressionparser.expressions.ToBooleanExpression;
|
||||
import ch.andre601.expressionparser.expressions.ToDoubleExpression;
|
||||
+241
-108
@@ -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,9 +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.List;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
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>{
|
||||
@@ -54,183 +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 HttpClient client = HttpClient.newHttpClient();
|
||||
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, "Returning local Favicon '%s'.", input);
|
||||
|
||||
return localFavicons.get(input.toLowerCase(Locale.ROOT)).favicon();
|
||||
}
|
||||
|
||||
if(input.equalsIgnoreCase("random")){
|
||||
logger.debug(FaviconHandler.class, "Input matches 'random'. Returning random Favicon...");
|
||||
return getRandomized();
|
||||
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();
|
||||
}
|
||||
|
||||
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));
|
||||
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);
|
||||
}
|
||||
|
||||
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 '<white>%s</white>'.", ex, input);
|
||||
return null;
|
||||
}
|
||||
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")){
|
||||
return CompletableFuture.completedFuture(getRandomized());
|
||||
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 F getRandomized(){
|
||||
logger.debug(FaviconHandler.class, "Obtaining random Favicon...");
|
||||
private FaviconPair<F> randomFavicon(){
|
||||
if(localFavicons.isEmpty())
|
||||
return null;
|
||||
|
||||
List<F> faviconList = List.copyOf(localFavicons.values());
|
||||
List<FaviconPair<F>> favicons = new ArrayList<>(localFavicons.values());
|
||||
if(favicons.isEmpty())
|
||||
return null;
|
||||
|
||||
// Don't use Random for just one entry.
|
||||
if(faviconList.size() == 1)
|
||||
return faviconList.get(0);
|
||||
if(favicons.size() == 1)
|
||||
return favicons.getFirst();
|
||||
|
||||
synchronized(random){
|
||||
return faviconList.get(random.nextInt(faviconList.size()));
|
||||
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.debugSuccess(FaviconHandler.class, "Created favicons folder.");
|
||||
logger.debugSuccess(FaviconHandler.class, "Created favicons folder");
|
||||
}catch(IOException ex){
|
||||
logger.failure("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>%d</white> local Favicons.", localFavicons.size());
|
||||
logger.success("Loaded <white>%s</white> local Favicon(s).", localFavicons.size());
|
||||
}catch(IOException ex){
|
||||
logger.failure("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.failure("Cannot create Favicon from file '<white>%s</white>'. 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.debugSuccess(FaviconHandler.class, "Loaded file '<white>%s</white>' 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.failure("Cannot create Favicon from file '<white>%s</white>'. 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 '<white>%s</white>'. 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 '<white>%s</white>'. Encountered a URISyntaxException.", 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){}
|
||||
}
|
||||
+8
-8
@@ -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;
|
||||
}
|
||||
@@ -103,7 +103,7 @@ public class PlayerHandler{
|
||||
return;
|
||||
}
|
||||
|
||||
if(core.getFileHandler().getBoolean("disableCache")){
|
||||
if(core.fileHandler().getBoolean("disableCache")){
|
||||
logger.debug(PlayerHandler.class, "[-] 'disable_cache' is set to true. Not saving cached players.");
|
||||
return;
|
||||
}
|
||||
@@ -117,14 +117,14 @@ public class PlayerHandler{
|
||||
}
|
||||
|
||||
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"))
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
+7
-14
@@ -78,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;
|
||||
|
||||
@@ -120,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){
|
||||
|
||||
+25
-2
@@ -30,13 +30,18 @@ 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 ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.tokens.PlaceholderToken;
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.PlaceholderToken;
|
||||
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();
|
||||
@@ -148,4 +153,22 @@ public class StringReplacer{
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,19 @@ printBanner: true
|
||||
#
|
||||
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.
|
||||
#
|
||||
|
||||
@@ -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,33 +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. "random" to display a random PNG from the favicons folder.
|
||||
# 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 placeholders returning one of these.
|
||||
# https://mc-heads.net will be used to retrieve an image of the provided name/UUID.
|
||||
# 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.
|
||||
#
|
||||
# Note on favicons not showing:
|
||||
# AdvancedServerList has to create a Favicon object for the Proxy/Server to use which takes time to do so.
|
||||
# To not delay the Proxy/Server from being displayed (increase the loading time on the player end) is this creation
|
||||
# done asynchronously for URL/Player name/UUID. This has the downside that the Proxy/Server may be done handling the
|
||||
# ping before the Favicon is created, causing none to be displayed.
|
||||
# Note that this won't happen with local favicons, as those are created during the plugin's startup.
|
||||
#
|
||||
# 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:
|
||||
#
|
||||
@@ -91,7 +97,7 @@ 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
|
||||
#
|
||||
@@ -100,17 +106,17 @@ playerCount:
|
||||
#
|
||||
# If not present, defaults to false.
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/#hideplayershover
|
||||
# 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'
|
||||
@@ -124,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: ''
|
||||
#
|
||||
@@ -133,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:
|
||||
#
|
||||
@@ -143,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
|
||||
#
|
||||
@@ -155,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
|
||||
#
|
||||
@@ -163,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:
|
||||
#
|
||||
@@ -172,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
|
||||
#
|
||||
@@ -181,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:
|
||||
#
|
||||
@@ -196,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
|
||||
#
|
||||
@@ -205,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
|
||||
@@ -25,27 +25,35 @@
|
||||
|
||||
package ch.andre601.advancedserverlist.core;
|
||||
|
||||
import ch.andre601.advancedserverlist.core.profiles.conditions.ProfileConditionParser;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
|
||||
import ch.andre601.advancedserverlist.api.objects.GenericServer;
|
||||
import ch.andre601.expressionparser.DefaultExpressionParserEngine;
|
||||
import ch.andre601.expressionparser.ParseWarnCollector;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class ExpressionParserTests{
|
||||
|
||||
private final ProfileConditionParser parser = ProfileConditionParser.create();
|
||||
private final DefaultExpressionParserEngine parser = DefaultExpressionParserEngine.createDefault();
|
||||
private final DummyPluginLogger logger = new DummyPluginLogger();
|
||||
private final Map<String, Boolean> testValuesMap = getTestMap();
|
||||
|
||||
private final TestPlayer player = new TestPlayer();
|
||||
private final TestServer server = new TestServer();
|
||||
|
||||
@Test
|
||||
public void testBasicExpressionResults(){
|
||||
for(Map.Entry<String, Boolean> values : testValuesMap.entrySet()){
|
||||
logger.info("TEST[expression=\"%s\", expected=%b]", values.getKey(), values.getValue());
|
||||
|
||||
ParseWarnCollector collector = new ParseWarnCollector(values.getKey());
|
||||
boolean result = parser.evaluate(values.getKey(), null, null, collector);
|
||||
boolean result = parser.compileToBoolean(values.getKey(), collector, player, server);
|
||||
|
||||
logger.info("Result (Output, Expected?): %b, %b", result, (result == values.getValue()));
|
||||
assertEquals(result, values.getValue());
|
||||
@@ -66,4 +74,38 @@ public class ExpressionParserTests{
|
||||
"false or 100 < 10", false
|
||||
);
|
||||
}
|
||||
|
||||
private static class TestPlayer implements GenericPlayer{
|
||||
@Override
|
||||
public @NonNull String getName(){
|
||||
return "Lorem";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocol(){
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull UUID getUUID(){
|
||||
return UUID.randomUUID();
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestServer implements GenericServer{
|
||||
@Override
|
||||
public int getPlayersOnline(){
|
||||
return 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayersMax(){
|
||||
return 20;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getHost(){
|
||||
return "play.example.com";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -19,11 +19,11 @@
|
||||
{% endif %}
|
||||
|
||||
{% if enum.description %}
|
||||
<p class="api-detail__description">
|
||||
<div class="api-detail__description" markdown>
|
||||
|
||||
{{ enum.description }}
|
||||
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block announce %}
|
||||
<span class="twemoji">{% include ".icons/material/party-popper.svg" %}</span>
|
||||
Celebrating <a href="https://andre601.ch/blog/2024/12-13-history-of-asl/" target="_blank" rel="nofollow"><b>25,000 downloads!</b></a> Thank you!
|
||||
<span class="twemoji">{% include ".icons/material/party-popper.svg" %}</span>
|
||||
<span class="rainbow"><span class="balloon twemoji">{% include ".icons/material/balloon.svg" %}</span> <b>100 THOUSAND downloads</b> on <a class="modrinth" href="https://modrinth.com/plugin/advancedserverlist" target="_blank" rel="nofollow"><span class="twemoji">{% include ".icons/simple/modrinth.svg" %}</span> <b>Modrinth!</b></a> <span class="balloon2 twemoji">{% include ".icons/material/balloon.svg" %}</span><br><b>THANK YOU!</b></span>
|
||||
{% endblock %}
|
||||
@@ -1,6 +1,6 @@
|
||||
[discord]: https://discord.gg/MyPTjpsgGH
|
||||
|
||||
[placeholderapi]: https://hangar.papermc.io/HelpChat/PlaceholderAPI
|
||||
[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
|
||||
|
||||
@@ -16,8 +16,7 @@ The BanPlugins Addon is available in downloads of AdvancedServerList on [:simple
|
||||
|
||||
**Supported Platforms:**
|
||||
|
||||
- :fontawesome-solid-paper-plane: Paper
|
||||
- :simple-spigotmc: BungeeCord
|
||||
<!-- icon:paper --> <!-- icon:bungee -->
|
||||
|
||||
| Placeholder | Description |
|
||||
|---------------------------------|------------------------------------------------------------------------------------------|
|
||||
@@ -57,9 +56,7 @@ motd:
|
||||
|
||||
**Supported Platforms:**
|
||||
|
||||
- :fontawesome-solid-paper-plane: Paper
|
||||
- :simple-spigotmc: BungeeCord
|
||||
- :fontawesome-solid-paper-plane: Velocity
|
||||
<!-- icon:all -->
|
||||
|
||||
| Placeholder | Description |
|
||||
|---------------------------------|------------------------------------------------------------------------------------------|
|
||||
@@ -95,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!"
|
||||
```
|
||||
///
|
||||
@@ -19,6 +19,12 @@ These Addons are made by Andre601 and available together with the plugin downloa
|
||||
|
||||
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
|
||||
|
||||
+7
-3
@@ -18,11 +18,13 @@ The plugin [SayanVanish]{ target="_blank" rel="nofollow" } is hooking into Advan
|
||||
| `${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 | :fontawesome-solid-paper-plane: Velocity
|
||||
/// tab | :simple-velocity: Velocity
|
||||
[:octicons-file-code-24: Source][source-velocity]
|
||||
|
||||
| Placeholder | Description |
|
||||
@@ -32,8 +34,10 @@ The plugin [SayanVanish]{ target="_blank" rel="nofollow" } is hooking into Advan
|
||||
| `${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/ffd740832fd914c6462c41917a91f575f43ee778/sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/hook/FeatureHookAdvancedServerList.kt
|
||||
[source-velocity]: https://github.com/Syrent/SayanVanish/blob/585279bf5480542858a9d3d91cfc4d844982aa0f/sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/feature/features/hook/FeatureHookAdvancedServerList.kt
|
||||
[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
|
||||
@@ -3,7 +3,7 @@ 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/index.md#condition) of the plugin.
|
||||
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).
|
||||
|
||||
@@ -101,7 +101,7 @@ public class MyPlugin extends Plugin {
|
||||
```
|
||||
///
|
||||
|
||||
/// tab | :fontawesome-solid-paper-plane: Velocity Example
|
||||
/// tab | :simple-velocity: Velocity Example
|
||||
```java title="MyPlugin.java"
|
||||
@Plugin(
|
||||
id = "myplugin",
|
||||
|
||||
+5
-53
@@ -88,12 +88,10 @@ dependencies {
|
||||
|
||||
Dedicated pages to explain aspects of the API are available:
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
/// html | div.grid.cards
|
||||
- [:octicons-plus-circle-24: Adding Placeholders](adding-placeholders.md)
|
||||
- [:octicons-broadcast-24: Listening for Events](listening-for-events.md)
|
||||
|
||||
</div>
|
||||
///
|
||||
|
||||
## Declare AdvancedServerList as (soft)depend
|
||||
|
||||
@@ -135,7 +133,7 @@ dependencies:
|
||||
////
|
||||
///
|
||||
|
||||
/// tab | :octicons-git-merge-24: BungeeCord
|
||||
/// tab | :simple-spigotmc: BungeeCord
|
||||
//// tab | Softdepend
|
||||
```yaml title="bungee.yml"
|
||||
name: "MyPlugin"
|
||||
@@ -163,7 +161,7 @@ depends:
|
||||
////
|
||||
///
|
||||
|
||||
/// tab | :octicons-git-merge-24: Velocity
|
||||
/// tab | :simple-velocity: Velocity
|
||||
//// tab | Softdepend (File)
|
||||
```json title="velocity-plugin.json"
|
||||
{
|
||||
@@ -251,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
|
||||
[iscancelled]: reference/api/ch.andre601.advancedserverlist.api/events/genericserverlistevent.md#iscancelled
|
||||
[setcancelled]: reference/api/ch.andre601.advancedserverlist.api/events/genericserverlistevent.md#setcancelled
|
||||
|
||||
### 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.
|
||||
@@ -7,12 +7,10 @@ These events are structurally the same across all platforms, yet still require t
|
||||
|
||||
The plugin currently offers the following events:
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
/// html | div.grid.cards
|
||||
- [PreServerListSetEvent](#preserverlistsetevent)
|
||||
- [PostServerListSetEvent](#postserverlistsetevent)
|
||||
|
||||
</div>
|
||||
///
|
||||
|
||||
## PreServerListSetEvent
|
||||
|
||||
@@ -22,12 +20,16 @@ This event is executed before AdvancedServerList tries to modify the player list
|
||||
|
||||
The following methods are available to use:
|
||||
|
||||
| Method | Description |
|
||||
|--------------------------|--------------------------------------------------------------------------------|
|
||||
| `getEntry()` | Returns the [`ProfileEntry`][profileentry] that AdvancedServerList should use. |
|
||||
| `setEntry(ProfileEntry)` | Sets the [`ProfileEntry`][profileentry] that should be used. |
|
||||
| `isCancelled()` | Returns wether this event was cancelled or not. |
|
||||
| `setCancelled(boolean)` | Sets the event's cancelled state. |
|
||||
| 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 }
|
||||
|
||||
@@ -41,7 +43,9 @@ This event is executed after AdvancedServerList completed the server list modifi
|
||||
|
||||
| Method | Description |
|
||||
|--------------------------|--------------------------------------------------------------------------|
|
||||
| `getEntry()` | Returns the [`ProfileEntry`][profileentry] that AdvancedServerList used. |
|
||||
| [`getEntry()`][getentry] | Returns the [`ProfileEntry`][profileentry] that AdvancedServerList used. |
|
||||
|
||||
[getentry]: reference/api/ch.andre601.advancedserverlist.api/events/genericserverlistevent.md#getentry
|
||||
|
||||
### Notes { #postserverlistsetevent-notes }
|
||||
|
||||
@@ -50,7 +54,107 @@ This event is executed after AdvancedServerList completed the server list modifi
|
||||
|
||||
## Registering the Events
|
||||
|
||||
To register an event, please refer to our platforms documentation on how to do this.
|
||||
/// 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?
|
||||
|
||||
@@ -60,6 +164,7 @@ The `ProfileEntry` is a record, representing the values of a [Server List Profil
|
||||
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()` exists to create a Builder instance of the ProfileEntry with its values already pre-set.
|
||||
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
|
||||
[profileentry]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/index.md
|
||||
[builder]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/index.md#builder
|
||||
+2
@@ -3,9 +3,11 @@ 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
|
||||
|
||||
@@ -5,17 +5,15 @@ methods:
|
||||
- name: 'getName'
|
||||
description: |
|
||||
Returns the name of the player.
|
||||
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.
|
||||
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 be whatever has been defined in AdvancedServerList's config.yml, should the player not be cached yet by the plugin.
|
||||
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.
|
||||
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
|
||||
@@ -23,7 +21,7 @@ methods:
|
||||
description: |
|
||||
Returns the unique ID associated with this player.
|
||||
|
||||
UUID may be whatever has been defined in AdvancedServerList's config.yml, should the player not be cached yet by AdvancedServerList.
|
||||
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,10 +30,10 @@ 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)
|
||||
|
||||
@@ -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)
|
||||
@@ -48,4 +48,4 @@ methods:
|
||||
|
||||
# <api__enum></api__enum> NullBool
|
||||
|
||||
Enum used to return a `Boolean` value with a default one should no value be set.
|
||||
Enum used to return a [`Boolean`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Boolean.html){ target="_blank" rel="noopener" } Value with a fallback, should the boolean be `null`.
|
||||
@@ -14,7 +14,7 @@ constructors:
|
||||
methods:
|
||||
- name: 'parsePlaceholder'
|
||||
description: |
|
||||
Method called by AdvancedServerList's StringReplacer class to replace any appearances of `${<identifier> <placeholder> with whatever value a matching PlaceholderProvider may return.
|
||||
Method called by AdvancedServerList's StringReplacer class to replace any appearances of `${<identifier> <placeholder>}` with whatever value a matching PlaceholderProvider may return.
|
||||
|
||||
Returning `null` will be treated as an invalid placeholder by the plugin, making it return the placeholder as-is without any changes.
|
||||
parameters:
|
||||
|
||||
File renamed without changes.
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
@@ -1,7 +1,4 @@
|
||||
document$.subscribe(async () => {
|
||||
const url = 'https://api.allorigins.win/raw?url='
|
||||
const apiUrl = `https://api.allorigins.win/raw?url=${encodeURIComponent('https://codeberg.org/api/v1/repos/Andre601/asl-api/releases/latest')}`
|
||||
|
||||
const repo_stats = document.querySelector('[data-md-component="source"] .md-source__repository');
|
||||
|
||||
async function loadCodebergInfo(data) {
|
||||
@@ -61,7 +58,7 @@ document$.subscribe(async () => {
|
||||
}
|
||||
|
||||
async function fetchApiInfo() {
|
||||
const tag = await fetch(`${apiUrl}`)
|
||||
const tag = await fetch('https://codeberg.org/api/v1/repos/Andre601/asl-api/releases/latest')
|
||||
.then(_ => _.json());
|
||||
|
||||
const data = {
|
||||
@@ -74,8 +71,8 @@ document$.subscribe(async () => {
|
||||
|
||||
async function fetchInfo() {
|
||||
const [release, repo] = await Promise.all([
|
||||
fetch(`${url}${encodeURIComponent('https://codeberg.org/api/v1/repos/Andre601/AdvancedServerList/releases/latest')}`).then(_ => _.json()),
|
||||
fetch(`${url}${encodeURIComponent('https://codeberg.org/api/v1/repos/Andre601/AdvancedServerList')}`).then(_ => _.json()),
|
||||
fetch('https://codeberg.org/api/v1/repos/Andre601/AdvancedServerList/releases/latest').then(_ => _.json()),
|
||||
fetch('https://codeberg.org/api/v1/repos/Andre601/AdvancedServerList').then(_ => _.json()),
|
||||
]);
|
||||
|
||||
const data = {
|
||||
|
||||
@@ -55,4 +55,136 @@
|
||||
|
||||
.md-banner__inner.md-grid.md-typeset {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.md-typeset .md-button.md-button--icon {
|
||||
padding: .5em;
|
||||
line-height: 0;
|
||||
color: var(--md-default-fg-color);
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
@keyframes balloon {
|
||||
0% {
|
||||
transform: translate(0rem, 0rem) rotate(0deg) scale(2);
|
||||
}
|
||||
10% {
|
||||
transform: translate(0.05rem, -0.08rem) rotate(2deg) scale(1.95);
|
||||
}
|
||||
20% {
|
||||
transform: translate(-0.07rem, 0.06rem) rotate(-3deg) scale(2);
|
||||
}
|
||||
30% {
|
||||
transform: translate(0.1rem, -0.02rem) rotate(1.5deg) scale(2.05);
|
||||
}
|
||||
40% {
|
||||
transform: translate(-0.06rem, -0.1rem) rotate(-2deg) scale(1.95);
|
||||
}
|
||||
50% {
|
||||
transform: translate(0.02rem, 0.08rem) rotate(3deg) scale(2);
|
||||
}
|
||||
60% {
|
||||
transform: translate(-0.1rem, -0.01rem) rotate(-1deg) scale(2.05);
|
||||
}
|
||||
70% {
|
||||
transform: translate(0.08rem, 0.04rem) rotate(2.5deg) scale(1.95);
|
||||
}
|
||||
80% {
|
||||
transform: translate(-0.03rem, -0.07rem) rotate(-2.5deg) scale(2);
|
||||
}
|
||||
90% {
|
||||
transform: translate(0.06rem, 0.03rem) rotate(1deg) scale(2.05);
|
||||
}
|
||||
100% {
|
||||
transform: translate(0rem, 0rem) rotate(0deg) scale(2);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes balloon2 {
|
||||
0% {
|
||||
transform: translate(0rem, 0rem) rotate(0deg) scale(2);
|
||||
}
|
||||
10% {
|
||||
transform: translate(0.02rem, -0.04rem) rotate(5deg) scale(1.75);
|
||||
}
|
||||
20% {
|
||||
transform: translate(-0.09rem, 0.08rem) rotate(-1deg) scale(2);
|
||||
}
|
||||
30% {
|
||||
transform: translate(0.05rem, -0.06rem) rotate(3deg) scale(2.2);
|
||||
}
|
||||
40% {
|
||||
transform: translate(-0.03rem, -0.09rem) rotate(-1.5deg) scale(1.85);
|
||||
}
|
||||
50% {
|
||||
transform: translate(0.04rem, 0.1rem) rotate(1deg) scale(2);
|
||||
}
|
||||
60% {
|
||||
transform: translate(-0.5rem, -0.04rem) rotate(-2deg) scale(1.95);
|
||||
}
|
||||
70% {
|
||||
transform: translate(0.06rem, 0.02rem) rotate(1.25deg) scale(2.05);
|
||||
}
|
||||
80% {
|
||||
transform: translate(-0.01rem, -0.03rem) rotate(-3deg) scale(2);
|
||||
}
|
||||
90% {
|
||||
transform: translate(0.02rem, 0.07rem) rotate(0.5deg) scale(2.15);
|
||||
}
|
||||
100% {
|
||||
transform: translate(0rem, 0rem) rotate(0deg) scale(2);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rainbow {
|
||||
0% {
|
||||
color: #ff6b6b;
|
||||
}
|
||||
10% {
|
||||
color: #ffcc70;
|
||||
}
|
||||
20% {
|
||||
color: #f7f48b;
|
||||
}
|
||||
30% {
|
||||
color: #caffbf;
|
||||
}
|
||||
40% {
|
||||
color: #9bf6ff;
|
||||
}
|
||||
50% {
|
||||
color: #a0c4ff;
|
||||
}
|
||||
60% {
|
||||
color: #bdb2ff;
|
||||
}
|
||||
70% {
|
||||
color: #ffc6ff;
|
||||
}
|
||||
80% {
|
||||
color: #ffadad;
|
||||
}
|
||||
90% {
|
||||
color: #ffd6a5;
|
||||
}
|
||||
100% {
|
||||
color: #ff6b6b;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.balloon {
|
||||
animation: 20s ease-in-out infinite balloon;
|
||||
}
|
||||
|
||||
.balloon2 {
|
||||
animation: 19s ease-in-out infinite balloon2;
|
||||
}
|
||||
|
||||
.rainbow {
|
||||
animation: 10s ease-in-out infinite rainbow;
|
||||
}
|
||||
|
||||
a.modrinth {
|
||||
color: #1bd96a;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: /asl clearcache
|
||||
---
|
||||
|
||||
# `/asl clearcache`
|
||||
|
||||
/// info |
|
||||
**Permissions:**
|
||||
|
||||
- `advancedserverlist.admin`
|
||||
- `advancedserverlist.command.clearcache`
|
||||
///
|
||||
|
||||
Clears currently loaded favicons and the player cache and loads local favicons again.
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
title: /asl help
|
||||
---
|
||||
|
||||
# `/asl help [query]`
|
||||
|
||||
/// info |
|
||||
**Permissions:**
|
||||
|
||||
- `advancedserverlist.admin`
|
||||
- `advancedserverlist.command.help`
|
||||
|
||||
**Arguments:**
|
||||
|
||||
- `[query]` - Optional search query to use
|
||||
///
|
||||
|
||||
Displays information about provided commands.
|
||||
If no query is provided will it list all commands. A page number can be used as query to switch between pages.
|
||||
+3
-73
@@ -4,78 +4,8 @@ icon: octicons/rel-file-path-24
|
||||
|
||||
# Commands
|
||||
|
||||
The plugin adds commands to use for various things related to the plugin itself.
|
||||
The main command is `/advancedserverlist` but an alias called `/asl` is also registered to use.
|
||||
AdvancedServerList adds commands that can be accessed via `/advancedserverlist` or `/asl` and provide different functionalities.
|
||||
|
||||
## Permissions
|
||||
## Available commands
|
||||
|
||||
The main permission is `advancedserverlist.admin` for all commands, but you can also grant access to specific commands using `advancedserverlist.command.<subcommand>` instead (i.e. `advancedserverlist.command.help` to grant access to [`/asl help [query]`](#help)).
|
||||
|
||||
## Subcommands
|
||||
|
||||
The following subcommands are available (Subcommands are case-insensitive):
|
||||
|
||||
- [`help [query]`](#help)
|
||||
- [`reload`](#reload)
|
||||
- [`clearCache`](#clearcache)
|
||||
- [`migrate <plugin>`](#migrate)
|
||||
- [`profiles {add <name> | copy <profile> <name> | info <profile> | list | set <profile> <option> [value]}`](#profiles)
|
||||
|
||||
### `help [query]` { #help }
|
||||
|
||||
**Permission:** `advancedserverlist.command.help`
|
||||
**Arguments:**
|
||||
|
||||
- `[query]` - Optional search query to receive help info from.
|
||||
|
||||
Shows a list of all available [subcommands](#subcommands) for AdvancedServerList.
|
||||
|
||||
----
|
||||
|
||||
### `reload`
|
||||
|
||||
**Permission:** `advancedserverlist.command.reload`
|
||||
|
||||
Reloads the plugin's `config.yml` and all available YAML files in the `profiles` folder.
|
||||
|
||||
----
|
||||
|
||||
### `clearcache`
|
||||
|
||||
**Permission:** `advancedserverlist.command.clearcache`
|
||||
|
||||
Clears the currently cached favicons and players.
|
||||
|
||||
----
|
||||
|
||||
### `migrate <plugin>` { #migrate }
|
||||
|
||||
**Permission:** `advancedserverlist.command.migrate`
|
||||
**Arguments:**
|
||||
|
||||
- `<plugin>` - The plugin to migrate from.
|
||||
|
||||
Migrates files from one plugin over to AdvancedServerList.
|
||||
Check the [Migration Page](../migration/index.md) for a list of supported plugins.
|
||||
|
||||
----
|
||||
|
||||
### `profiles {add <profile> | copy <profile> <name> | info <profile> | list | set <profile> <option> [value]}` { #profiles }
|
||||
|
||||
**Permission:** `advancedserverlist.command.profiles`
|
||||
**Arguments:**
|
||||
|
||||
- `add <profile>` - Creates a new profile using the default profile values.
|
||||
- `<profile>` - Name of the profile to use.
|
||||
- `copy <profile> <name>` - Creates a copy of an existing profile.
|
||||
- `<profile>` - Name of the profile to copy from.
|
||||
- `<name>` - Name of the copy to have.
|
||||
- `info <profile>` - Returns information of a profile. Only executable by players.
|
||||
- `<profile>` - Name of the profile to fetch info from.
|
||||
- `list` - Lists all loaded profiles. The entries have hover text for extra info.
|
||||
- `set <profile> <option> [value]` - Edits values of a profile. Requires [`/asl reload`](#reload) to apply changes.
|
||||
- `<profile>` - Name of the profile to edit.
|
||||
- `<option>` - The option to edit.
|
||||
- `[value]` - The new value to set. Leave empty to reset to the default.
|
||||
|
||||
Creates, copies or shows profiles in AdvancedServerList.
|
||||
{nav}
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
title: /asl migrate
|
||||
---
|
||||
|
||||
# `/asl migrate <plugin>`
|
||||
|
||||
/// info |
|
||||
**Permissions:**
|
||||
|
||||
- `advancedserverlist.admin`
|
||||
- `advancedserverlist.command.`
|
||||
|
||||
**Arguments:**
|
||||
|
||||
- `<plugin>` - The plugin to migrate from.
|
||||
///
|
||||
|
||||
Migrates configuration from the provided plugin to AdvancedServerList.
|
||||
A list of supported plugins and known limitations can be found on the [Migration page](../migration/index.md).
|
||||
@@ -0,0 +1,129 @@
|
||||
---
|
||||
title: /asl profiles
|
||||
---
|
||||
|
||||
# `/asl profiles add <profile>`<br>`/asl profiles copy <profile> <name>`<br>`/asl profiles edit <profile>`<br>`/asl profiles info <profile>`<br>`/asl profiles list`<br>`/asl profiles set <profile> <option> [value]`
|
||||
|
||||
/// info |
|
||||
**Permissions:**
|
||||
|
||||
- `advancedserverlist.admin`
|
||||
- `advancedserverlist.command.profiles`
|
||||
|
||||
**Subcommands:**
|
||||
|
||||
- [`add <profile>`](#add)
|
||||
- [`copy <profile> <name>`](#copy)
|
||||
- [`info <profile>`](#info)
|
||||
- [`list`](#list)
|
||||
- [`set <profile> <option> [value]`](#set)
|
||||
///
|
||||
|
||||
Creates, copies, lists (info of) or sets values for profiles.
|
||||
|
||||
## `/asl profiles add <profile>` { #add }
|
||||
|
||||
/// info |
|
||||
**Permissions:**
|
||||
|
||||
- `advancedserverlist.admin`
|
||||
- `advancedserverlist.command.profiles`
|
||||
- `advancedserverlist.command.profiles.add`
|
||||
|
||||
**Arguments:**
|
||||
|
||||
- `<profile>` - Name of the profile to create.
|
||||
///
|
||||
|
||||
Creates a new profile using default values.
|
||||
[`/asl reload`](reload.md) needs to be executed to load the new profile.
|
||||
|
||||
## `/asl profiles copy <profile> <name>` { #copy }
|
||||
|
||||
/// info |
|
||||
**Permissions:**
|
||||
|
||||
- `advancedserverlist.admin`
|
||||
- `advancedserverlist.command.profiles`
|
||||
- `advancedserverlist.command.profiles.copy`
|
||||
|
||||
**Arguments:**
|
||||
|
||||
- `<profile>` - Name of the profile to copy.
|
||||
- `<name>` - Name of the copy to make.
|
||||
///
|
||||
|
||||
Creates a copy from the provided profile.
|
||||
[`/asl reload`](reload.md) needs to be executed to load the new profile.
|
||||
|
||||
## `/asl profiles edit <profile>` { #edit }
|
||||
|
||||
/// info |
|
||||
**Permissions:**
|
||||
|
||||
- `advancedserverlist.admin`
|
||||
- `advancedserverlist.command.profiles`
|
||||
- `advancedserverlist.command.profiles.edit`
|
||||
|
||||
**Arguments:**
|
||||
|
||||
- `<profile>` - Name of the profile to edit.
|
||||
///
|
||||
|
||||
Opens a Dialog to edit the specified Profile.
|
||||
The first Dialog allows you to select either the primary settings or the individual `profiles` with each option allowing you to edit their values according to what they support.
|
||||
|
||||
/// warning
|
||||
This Feature only works for `v5.7.0-b1` and newer of AdvancedServerList and only for Paper 1.21.7 Builds 17 and newer due to lack of API in older versions.
|
||||
///
|
||||
|
||||
## `/asl profiles info <profile>` { #info }
|
||||
|
||||
/// info |
|
||||
**Permissions:**
|
||||
|
||||
- `advancedserverlist.admin`
|
||||
- `advancedserverlist.command.profiles`
|
||||
- `advancedserverlist.command.profiles.info`
|
||||
|
||||
**Arguments:**
|
||||
|
||||
- `<profile>` - The profile to list info from.
|
||||
///
|
||||
|
||||
<!-- admo:info Only executable by players. -->
|
||||
|
||||
Lists currently set values for the provided profile.
|
||||
Each option has hover text to show the value and a click action to receive the matching [`/asl profiles set ...`](#set) command.
|
||||
|
||||
## `/asl profiles list` { #list }
|
||||
|
||||
/// info |
|
||||
**Permissions:**
|
||||
|
||||
- `advancedserverlist.admin`
|
||||
- `advancedserverlist.command.profiles`
|
||||
- `advancedserverlist.command.profiles.list`
|
||||
///
|
||||
|
||||
Lists all currently loaded profiles.
|
||||
Each profile has a hover text displaying their priority, condition and whether they are considered valid.
|
||||
|
||||
## `/asl profiles set <profile> <option> [value]` { #set }
|
||||
|
||||
/// info |
|
||||
**Permissions:**
|
||||
|
||||
- `advancedserverlist.admin`
|
||||
- `advancedserverlist.command.profiles`
|
||||
- `advancedserverlist.command.profiles.set`
|
||||
|
||||
**Arguments:**
|
||||
|
||||
- `<profile>` - The profile to modify.
|
||||
- `<option>` - The option to modify.
|
||||
- `[value]` - Optional value to set. Leave empty to reset the option.
|
||||
///
|
||||
|
||||
Sets the config option `<option>` to the provided `[value]`. Leaving value empty results in the option being reset to an empty/unset state.
|
||||
[`/asl reload`](reload.md) needs to be executed to apply the changes.
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: /asl reload
|
||||
---
|
||||
|
||||
# `/asl reload`
|
||||
|
||||
/// info |
|
||||
**Permissions:**
|
||||
|
||||
- `advancedserverlist.admin`
|
||||
- `advancedserverlist.command.reload`
|
||||
///
|
||||
|
||||
Reloads the config.yml and any YAML file in the profiles folder.
|
||||
@@ -12,8 +12,7 @@ The guide is designed to be as easy to understand as possible. If you still have
|
||||
The first step would be to download the plugin from a download page of your choice.
|
||||
AdvancedServerList is available on the following places (Please note the current status!):
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
/// html | div.grid.cards
|
||||
- [:simple-modrinth: **Modrinth**][modrinth]{ target="_blank" rel="nofollow" }
|
||||
|
||||
----
|
||||
@@ -38,8 +37,7 @@ AdvancedServerList is available on the following places (Please note the current
|
||||
----
|
||||
|
||||
:octicons-alert-24:{ style="color: var(--md-badge-fg--warning);" } **Discontinued!**
|
||||
|
||||
</div>
|
||||
///
|
||||
|
||||
[spigot]: https://www.spigotmc.org/resources/102910/
|
||||
|
||||
@@ -47,8 +45,7 @@ The plugin is available for Paper, BungeeCord, Waterfall and Velocity, and has b
|
||||
|
||||
The plugin also supports these additional plugins. They are all optional.
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
/// html | div.grid.cards
|
||||
- [**PlaceholderAPI**][placeholderapi]{ target="_blank" rel="nofollow" }
|
||||
|
||||
----
|
||||
@@ -58,7 +55,7 @@ The plugin also supports these additional plugins. They are all optional.
|
||||
----
|
||||
|
||||
**Supported on:**
|
||||
:fontawesome-solid-paper-plane: Paper
|
||||
<!-- icon:paper -->
|
||||
|
||||
- [**ViaVersion**][viaversion]{ target="_blank" rel="nofollow" }
|
||||
|
||||
@@ -69,7 +66,7 @@ The plugin also supports these additional plugins. They are all optional.
|
||||
----
|
||||
|
||||
**Supported on:**
|
||||
:fontawesome-solid-paper-plane: Paper
|
||||
<!-- icon:paper -->
|
||||
|
||||
- [**PAPIProxyBridge**][papiproxybridge]{ target="_blank" rel="nofollow" }
|
||||
|
||||
@@ -80,7 +77,7 @@ The plugin also supports these additional plugins. They are all optional.
|
||||
----
|
||||
|
||||
**Supported on:**
|
||||
:simple-spigotmc: BungeeCord, :fontawesome-solid-paper-plane: Velocity
|
||||
<!-- icon:proxies -->
|
||||
|
||||
- [**Maintenance**][maintenance]{ target="_blank" rel="nofollow" }
|
||||
|
||||
@@ -91,9 +88,9 @@ The plugin also supports these additional plugins. They are all optional.
|
||||
----
|
||||
|
||||
**Supported on:**
|
||||
:fontawesome-solid-paper-plane: Paper, :simple-spigotmc: BungeeCord, :fontawesome-solid-paper-plane: Velocity
|
||||
<!-- icon:all -->
|
||||
|
||||
- [**BanPlugins Addon**][addon]
|
||||
- [**BanPlugins Addon**][banplugins-addon]
|
||||
|
||||
----
|
||||
|
||||
@@ -102,11 +99,22 @@ The plugin also supports these additional plugins. They are all optional.
|
||||
----
|
||||
|
||||
**Supported on:**
|
||||
:fontawesome-solid-paper-plane: Paper, :simple-spigotmc: BungeeCord, :fontawesome-solid-paper-plane: Velocity
|
||||
<!-- icon:all -->
|
||||
|
||||
</div>
|
||||
- [**LuckPerms Addon**][luckperms-addon]
|
||||
|
||||
----
|
||||
|
||||
[addon]: ../addons/first-party/banplugins.md
|
||||
Official Add-on for AdvancedServerList that adds placeholders for LuckPerms.
|
||||
|
||||
----
|
||||
|
||||
**Supported on:**
|
||||
<!-- icon:all -->
|
||||
///
|
||||
|
||||
[banplugins-addon]: ../addons/first-party/banplugins.md
|
||||
[luckperms-addon]: ../addons/first-party/luckperms.md
|
||||
|
||||
## 2. Installation { #installation }
|
||||
|
||||
@@ -116,7 +124,6 @@ Here is a quick table showing the jar file name with the platforms it is made fo
|
||||
| Jar file name | Platforms |
|
||||
|-------------------------------------------------------------------------------------|-----------------------|
|
||||
| `AdvancedServerList-Paper-{version}.jar`{ data-md-component="plugin-version" } | Paper |
|
||||
| `AdvancedServerList-BungeeCord-{version}.jar`{ data-md-component="plugin-version" } | BungeeCord, Waterfall |
|
||||
| `AdvancedServerList-Velocity-{version}.jar`{ data-md-component="plugin-version" } | Velocity |
|
||||
|
||||
After you added it to your plugins folder - alongside any other dependency you may want - (re)start your server or proxy to enable the plugin.
|
||||
@@ -136,7 +143,7 @@ Let's cover the files and folders...
|
||||
|
||||
#### Favicons folder
|
||||
|
||||
This folder allows you to store PNG images that should be used as favicons by the plugin. A favicon from this folder can be used by referencing the name (Including the `.png` file extension) in the [`favicon` option of the server list profile](../profiles/index.md#favicon).
|
||||
This folder allows you to store PNG images that should be used as favicons by the plugin. A favicon from this folder can be used by referencing the name (Including the `.png` file extension) in the [`favicon` option of the server list profile](../profiles/options/favicon.md).
|
||||
|
||||
#### Profiles folder and default.yml { #profiles-folder-and-default-yml }
|
||||
|
||||
@@ -159,9 +166,9 @@ 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
|
||||
#
|
||||
@@ -189,8 +196,11 @@ 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.
|
||||
#
|
||||
@@ -204,20 +214,14 @@ 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. "random" to display a random PNG from the favicons folder.
|
||||
# 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 placeholders returning one of these.
|
||||
# https://mc-heads.net will be used to retrieve an image of the provided name/UUID.
|
||||
#
|
||||
# Note on favicons not showing:
|
||||
# AdvancedServerList has to create a Favicon object for the Proxy/Server to use which takes time to do so.
|
||||
# To not delay the Proxy/Server from being displayed (increase the loading time on the player end) is this creation
|
||||
# done asynchronously for URL/Player name/UUID. This has the downside that the Proxy/Server may be done handling the
|
||||
# ping before the Favicon is created, causing none to be displayed.
|
||||
# Note that this won't happen with local favicons, as those are created during the plugin's startup.
|
||||
# 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.
|
||||
#
|
||||
# Any images above or below 64x64 pixels will be automatically resized to these dimensions.
|
||||
#
|
||||
@@ -225,6 +229,15 @@ motd:
|
||||
#
|
||||
# Read more: https://asl.andre601.ch/profiles/#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: ''
|
||||
|
||||
#
|
||||
@@ -255,7 +268,7 @@ playerCount:
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
@@ -448,6 +461,19 @@ printBanner: true
|
||||
#
|
||||
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.
|
||||
#
|
||||
@@ -516,7 +542,7 @@ disableDuringMaintenance:
|
||||
# This is used internally to determine if the config needs to be migrated.
|
||||
# Changing or even removing this option could result in your config being broken.
|
||||
#
|
||||
configVersion: 8
|
||||
configVersion: 10
|
||||
```
|
||||
///
|
||||
|
||||
@@ -535,14 +561,14 @@ There are certain files and folders that get created when certain events happen:
|
||||
|
||||
Creating your first profile is relatively simple.
|
||||
|
||||
It's best to open the `default.yml` file located inside `profiles` with the file editor of your choice ([:simple-vscodium: VSCodeium](https://vscodium.com/) or [:simple-notepadplusplus: Notepad++](https://notepad-plus-plus.org/) are recommended).
|
||||
It's best to open the `default.yml` file located inside `profiles` with the file editor of your choice ([:simple-vscodium: VSCodeium](https://vscodium.com/){ target="_blank" rel="nofollow" } or [:simple-notepadplusplus: Notepad++](https://notepad-plus-plus.org/){ target="_blank" rel="nofollow" } are recommended).
|
||||
This file should contain all available options that you can set and alter.
|
||||
|
||||
Edit the options to whatever you like. All text-based options, except [`condition`](../profiles/index.md#condition), support [MiniMessage formatting](../profiles/formatting.md).
|
||||
Hexadecimal (RGB) colors are supported, however, only the [`motd` option](../profiles/index.md#motd) may display them and only for MC 1.16+ clients. Any other option will have the color down-sampled to the nearest supported color code.
|
||||
Edit the options to whatever you like. All text-based options, except [`condition`](../profiles/options/condition.md), support [MiniMessage formatting](../profiles/formatting.md).
|
||||
Hexadecimal (RGB) colors are supported, however, only the [`motd` option](../profiles/options/motd.md) may display them and only for MC 1.16+ clients. Any other option will have the color down-sampled to the nearest supported color code.
|
||||
|
||||
If you're unsure how a specific option should look like, head over to the [Profiles page](../profiles/index.md) for more information about the general structure.
|
||||
All you need to know is, that a bare-bones server list profile requires a valid [priority](../profiles/index.md#priority) and at least one of the settings to be present.
|
||||
All you need to know is, that a bare-bones server list profile requires a valid [priority](../profiles/options/priority.md) and at least one of the other options (except condition) to be present.
|
||||
|
||||
To give an example is here a basic profile that only modifies the MOTD and nothing else:
|
||||
```yaml title="default.yml"
|
||||
@@ -555,10 +581,10 @@ motd:
|
||||
|
||||
### Additional profiles
|
||||
|
||||
Additional profiles can be created to display different content under specific situations. To create a new file, either manually create a YAML file in the `profiles` folder, or use [`/asl profiles add <name>`](../commands/index.md#profiles) to have one created for you. In either case is it recommended to only use alphanummeric (`a-z` abd `0-9`) characters, dashes and underscores for the file name. You should also choose a name that fits the purpose of the file (i.e. for a profile that displays stuff when someone is banned, name it `banned.yml`).
|
||||
Additional profiles can be created to display different content under specific situations. To create a new file, either manually create a YAML file in the `profiles` folder, or use [`/asl profiles add {name}`](../commands/profiles.md#add) to have one created for you. In either case is it recommended to only use alphanummeric (`a-z` abd `0-9`) characters, dashes and underscores for the file name. You should also choose a name that fits the purpose of the file (i.e. for a profile that displays stuff when someone is banned, name it `banned.yml`).
|
||||
|
||||
In case you manually created a YAML file, make sure it contains the [`priority`](../profiles/index.md#priority) setting and one of the other settings, to have a valid profile.
|
||||
You should also add a [`condition`](../profiles/index.md#condition) to ensure that the profile is only loaded when needed. The [Expressions page](../profiles/expressions.md) covers what counts as valid expressions and conditions.
|
||||
In case you manually created a YAML file, make sure it contains the [`priority`](../profiles/options/priority.md) setting and one of the other settings, to have a valid profile.
|
||||
You should also add a [`condition`](../profiles/options/condition.md) to ensure that the profile is only loaded when needed. The [Expressions page](../profiles/expressions.md) covers what counts as valid expressions and conditions.
|
||||
Do note that the plugin goes through the profiles in order of priority, starting with the highest number first, picking the first one that returns a `condition` returning true.
|
||||
|
||||
/// info | Note about Condition
|
||||
@@ -582,7 +608,7 @@ The [Examples page](../examples/index.md) contains more examples of different se
|
||||
## 4. Loading profiles { #loading-profiles }
|
||||
|
||||
Once you've set up your server list profile(s) is it time to load it/them.
|
||||
To do this, simply run `/asl reload` as player (Requires permission `advancedserverlist.admin` or `advancedserverlist.command.reload`) or `asl reload` through the Server/Proxy console.
|
||||
To do this, simply run [`/asl reload`](../commands/reload.md).
|
||||
|
||||
The plugin should then load any valid YAML file inside the `profiles` folder to then use.
|
||||
|
||||
|
||||
Loaded 100 of 164 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user