forked from upstream/Maintenance
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dbdf811ae5 | ||
|
|
0518f848e1 | ||
|
|
4ebfa8c750 | ||
|
|
ea51ea9c63 | ||
|
|
3049f17250 | ||
|
|
619da4abee | ||
|
|
bb838fd4dd | ||
|
|
695404ce5c | ||
|
|
fd30fa4da6 | ||
|
|
3aad351c7b | ||
|
|
95492fe0e1 | ||
|
|
11b8fd2861 | ||
|
|
b9fdc90f7f | ||
|
|
acc5d89263 | ||
|
|
46b9dc4e04 | ||
|
|
5d37138b64 | ||
|
|
83f3a850c5 | ||
|
|
b58cb9709b | ||
|
|
00f6b25a67 | ||
|
|
e5fd9e6f37 | ||
|
|
051d916f87 | ||
|
|
b1ace71d4b | ||
|
|
da6fef8906 | ||
|
|
f6ad07a115 | ||
|
|
9072e03f79 | ||
|
|
946c1a7f72 | ||
|
|
572fc63162 | ||
|
|
87aedfaa3b | ||
|
|
8aba97d60d | ||
|
|
04d85b9c3c | ||
|
|
0603a17b5e | ||
|
|
3cd689f15d | ||
|
|
6c28d31fec | ||
|
|
1988b0284b | ||
|
|
c7f3dc72d4 | ||
|
|
9767322376 | ||
|
|
0e50c39100 | ||
|
|
ad2228b9c4 | ||
|
|
90e880500a | ||
|
|
15c3d9c111 | ||
|
|
ec55606737 | ||
|
|
a7438ca297 | ||
|
|
65d68982f9 | ||
|
|
03f0368c10 | ||
|
|
c16637ee36 | ||
|
|
7034bb85f5 | ||
|
|
38b142e8c9 | ||
|
|
2af4929c3a | ||
|
|
035b720369 | ||
|
|
8a106e4b34 | ||
|
|
f4a34041b8 | ||
|
|
ceb812e025 | ||
|
|
5058e851d8 | ||
|
|
ba04bae7bf | ||
|
|
d1abdd496e | ||
|
|
956e7bb3ef | ||
|
|
171e1368f4 | ||
|
|
0fff12f368 | ||
|
|
a9d052eabb | ||
|
|
10c661e849 | ||
|
|
695f3872a0 | ||
|
|
05af3d3db2 | ||
|
|
f9396fa369 | ||
|
|
ddc43fb1eb | ||
|
|
5c51dd86b6 | ||
|
|
dcb5f64449 | ||
|
|
c0b991932c | ||
|
|
3b9ef8b006 | ||
|
|
c73e18acf1 | ||
|
|
55576dce4a | ||
|
|
aa1c77c230 | ||
|
|
72b5f38e63 | ||
|
|
3e8cc06aa7 | ||
|
|
0f375adce6 | ||
|
|
9c5e8182cc | ||
|
|
1b94594fc1 | ||
|
|
67bdc8261a | ||
|
|
e255529f78 | ||
|
|
5b7ab9fe42 | ||
|
|
cfa30cea79 | ||
|
|
c59b784e48 | ||
|
|
864fee66fa | ||
|
|
3c810d7ed3 | ||
|
|
e24806694e | ||
|
|
fa7effe8f3 | ||
|
|
26802cbe1a | ||
|
|
1cb35abccb | ||
|
|
1f2d0bba2a | ||
|
|
a7e577554c | ||
|
|
c0b3ea22b9 | ||
|
|
0adf767b5e | ||
|
|
3cc21504f0 | ||
|
|
cdebac351f | ||
|
|
8941b55362 | ||
|
|
fa225ad5f0 | ||
|
|
dea7fc9032 | ||
|
|
ced1651c05 | ||
|
|
6be27d05bb | ||
|
|
c39bb44e42 | ||
|
|
b71e1ce25d | ||
|
|
b5cfc47524 | ||
|
|
c32698fc76 | ||
|
|
b177c5e2f8 | ||
|
|
d0e0aff78e | ||
|
|
9f5ecd590e | ||
|
|
bc93f1f36d | ||
|
|
3c1c1e48fb | ||
|
|
c8e14e1d74 | ||
|
|
29300d5fe2 | ||
|
|
7da9365e23 | ||
|
|
ded84cd1c9 | ||
|
|
62cc71675b | ||
|
|
0752d3dc93 | ||
|
|
d352e44e50 | ||
|
|
83877a6c3a | ||
|
|
199eb2e67d | ||
|
|
0683450c2d | ||
|
|
dbdd7d18d0 | ||
|
|
cf4ae33ca7 | ||
|
|
ba8c574e79 | ||
|
|
225fe4b15a | ||
|
|
de203244c3 | ||
|
|
167065eef5 | ||
|
|
25503f6196 | ||
|
|
23bfe7ef8f | ||
|
|
31e721f443 | ||
|
|
158680e6ca | ||
|
|
b6c9c1bd5e | ||
|
|
370b9d9eb8 | ||
|
|
339136c1f2 | ||
|
|
4f0e158435 | ||
|
|
c6efc58242 | ||
|
|
6aed091356 | ||
|
|
d343c75eb6 | ||
|
|
9cbe2597d0 | ||
|
|
912d094d6d | ||
|
|
6d942f87ce | ||
|
|
30380604da | ||
|
|
f2c07f1fd4 | ||
|
|
36f14a6b30 | ||
|
|
871bd0882c | ||
|
|
fbc9fc47e2 | ||
|
|
ccd5eab475 | ||
|
|
5d0fbb310e | ||
|
|
09d2e03c0c | ||
|
|
57da60a669 | ||
|
|
63dafb05b6 | ||
|
|
d8e36d7ff6 | ||
|
|
39f966ba5d | ||
|
|
879f07e3e7 | ||
|
|
9cbead310a | ||
|
|
25375b184d | ||
|
|
eec5acd0ce | ||
|
|
14cef4d53b | ||
|
|
4d06d0f6b8 | ||
|
|
5e22098023 | ||
|
|
80cc51e48e | ||
|
|
15ebe045f6 | ||
|
|
be39524717 | ||
|
|
b8616d84c8 | ||
|
|
7515d95b82 | ||
|
|
2c0172d11e | ||
|
|
920e3477ee | ||
|
|
d8e0b6497a | ||
|
|
113895b564 | ||
|
|
99fffb5056 | ||
|
|
3d2aa0ba9c | ||
|
|
5486b9542a | ||
|
|
5c2ff9588f | ||
|
|
3bcce2dfb9 | ||
|
|
0db0d608e5 | ||
|
|
ae895f4db5 | ||
|
|
351d842f3a | ||
|
|
44dc76f381 | ||
|
|
48792413f7 | ||
|
|
a81ea8d87d | ||
|
|
ba142a1c31 | ||
|
|
f849724a8c | ||
|
|
5e17e60921 | ||
|
|
fad98d7a14 | ||
|
|
31da57c9b2 | ||
|
|
c1a03a6733 | ||
|
|
92ab4574e8 | ||
|
|
f81851ad9a | ||
|
|
6fb153eb9c | ||
|
|
a6e7bfa793 | ||
|
|
75656be4f5 | ||
|
|
f09f95549a | ||
|
|
78dc87e74a | ||
|
|
3503d02501 | ||
|
|
f8ea89b0b9 | ||
|
|
d0d672b76b | ||
|
|
616d4c7aba | ||
|
|
4415d036a2 | ||
|
|
1f70247c1d | ||
|
|
02ed575e3c | ||
|
|
4e005bca17 | ||
|
|
dcf61a4036 | ||
|
|
8e7b80bddf | ||
|
|
16775bda54 | ||
|
|
29d9fa2ec8 | ||
|
|
ef2ce99746 | ||
|
|
aa5437605f | ||
|
|
22e1d53f81 | ||
|
|
1359684c72 | ||
|
|
22c53bd4ff | ||
|
|
c0e2586684 | ||
|
|
2f095f32ca | ||
|
|
e1e4926f83 | ||
|
|
55b5bf2714 | ||
|
|
5f8f885530 | ||
|
|
fc40b0230d | ||
|
|
6fb79d7051 | ||
|
|
7bc522c1d3 | ||
|
|
8d0d97c220 | ||
|
|
e7c8430909 | ||
|
|
1646d4e417 | ||
|
|
f8384f4f99 | ||
|
|
4fd6ba33ff | ||
|
|
94c146d0e9 | ||
|
|
c624b18b8e | ||
|
|
18a00f9054 | ||
|
|
9778a900a7 | ||
|
|
21fa6a3b7f | ||
|
|
c7980cbc86 | ||
|
|
cdb6715744 | ||
|
|
d868f069cc | ||
|
|
c58b29a78f | ||
|
|
5fb4f06f4c | ||
|
|
8a3afdd1a8 | ||
|
|
36e561c413 | ||
|
|
4739176e9b | ||
|
|
9546cc7b74 | ||
|
|
740051dda5 | ||
|
|
e53f4f2518 | ||
|
|
03390d7be9 | ||
|
|
039c5a3d04 | ||
|
|
4360eb03e3 | ||
|
|
89691e8064 | ||
|
|
8f4860ad56 | ||
|
|
b8b231ce78 | ||
|
|
fd8b47a9f5 | ||
|
|
10c0b5bab3 | ||
|
|
cece2b5813 | ||
|
|
aa9a686b8a | ||
|
|
f29b38989c | ||
|
|
ad9c989213 | ||
|
|
f01e96a32a | ||
|
|
e247f4ef10 | ||
|
|
b8e0f84d54 | ||
|
|
424617c602 | ||
|
|
21ed266d3d | ||
|
|
a409ec5d66 | ||
|
|
66cba12fd3 | ||
|
|
f6d178d8d1 | ||
|
|
d04749c835 | ||
|
|
eebbc4d532 | ||
|
|
335fe303b5 | ||
|
|
1c4b5e62a4 | ||
|
|
d41fa5ab24 | ||
|
|
7fd486ef40 | ||
|
|
d0b10f2524 | ||
|
|
865df5e20a | ||
|
|
469e8eb57b | ||
|
|
368ac37099 | ||
|
|
0db4d77c10 | ||
|
|
ca4c711103 | ||
|
|
f4a5f288ca | ||
|
|
0a37bfdefe | ||
|
|
3a30dba011 | ||
|
|
111a229d48 | ||
|
|
7e948fbe52 | ||
|
|
355aa20244 | ||
|
|
6bc54d1812 | ||
|
|
0042ce3ccd | ||
|
|
beb0e52371 | ||
|
|
4774ec2f2a | ||
|
|
34115f443e | ||
|
|
c8f0c1699e | ||
|
|
f22be77023 | ||
|
|
730a8dbeea | ||
|
|
0548e0c447 | ||
|
|
da0a757c5e | ||
|
|
ac19bac11c | ||
|
|
1de926bb28 | ||
|
|
94308cde7c | ||
|
|
d863c709e1 | ||
|
|
741914283a | ||
|
|
64902d90ff | ||
|
|
d3b4c7c87b | ||
|
|
390b65a70e | ||
|
|
7cff908aae | ||
|
|
d1f049c3e5 | ||
|
|
8d552dbe33 | ||
|
|
d3a933d21d | ||
|
|
a4d87dabf5 | ||
|
|
c3b579e86c | ||
|
|
b75d2337df | ||
|
|
b3e83c5c2e | ||
|
|
b6f447aded | ||
|
|
9febdfc140 | ||
|
|
90bc30d701 | ||
|
|
843ba4ac86 | ||
|
|
032fd7d582 | ||
|
|
9a0f680177 | ||
|
|
415d5f3807 | ||
|
|
33c96de5fb | ||
|
|
d141b40190 | ||
|
|
77e1444dba | ||
|
|
915b43d897 | ||
|
|
adc06e3486 | ||
|
|
cc7f5c2d11 | ||
|
|
0bb54ffc5d | ||
|
|
15ee2e6074 | ||
|
|
89abab36f8 | ||
|
|
e0bb1336b0 | ||
|
|
888ffd301a | ||
|
|
3a55dd3280 | ||
|
|
61c47ba2fa | ||
|
|
b97e69a71f | ||
|
|
75c940d089 | ||
|
|
849fa2a720 | ||
|
|
7e249faa99 | ||
|
|
c8272b39d5 | ||
|
|
b3c561ae26 | ||
|
|
af24c5a331 | ||
|
|
4cc162f00d | ||
|
|
02610fd904 | ||
|
|
bccd1bb936 | ||
|
|
c08dd5d430 | ||
|
|
1104a50882 | ||
|
|
56898d7b40 | ||
|
|
4be31a6dd4 | ||
|
|
190a063d49 | ||
|
|
6e4591d45b | ||
|
|
86c556e841 | ||
|
|
404e14ee94 | ||
|
|
b8c0e3a799 | ||
|
|
10d798acd4 | ||
|
|
be057f4b3b | ||
|
|
eea7a595c9 | ||
|
|
bf4bf79b23 | ||
|
|
686535228e | ||
|
|
8b645b6f79 | ||
|
|
4d97293a8c | ||
|
|
c746a83f5f | ||
|
|
eb7e6e3f0b | ||
|
|
f5ce41853f | ||
|
|
e67cde591c | ||
|
|
39682635c6 | ||
|
|
efee532742 | ||
|
|
0aee3a8fd6 | ||
|
|
f79b6caccb | ||
|
|
73fd80b456 | ||
|
|
ad1a7eb420 | ||
|
|
bfa9b52497 | ||
|
|
ff4b5b0d2e | ||
|
|
b8e73062fa | ||
|
|
863a9b3beb | ||
|
|
fe64936ddf | ||
|
|
0c524b227a | ||
|
|
35deabf5a3 | ||
|
|
694cbb729f | ||
|
|
57047c519e | ||
|
|
fc8018c530 | ||
|
|
73707e2c3a | ||
|
|
ac92851e3d | ||
|
|
4e29a98a32 | ||
|
|
54d1c90795 | ||
|
|
6780914550 | ||
|
|
5ab5d11aed | ||
|
|
94378ca1fa | ||
|
|
4c0c425ba0 | ||
|
|
efea58e665 | ||
|
|
e15a7dd0ed | ||
|
|
2f327b9ce9 | ||
|
|
38d0f9de6d | ||
|
|
75f7330cc3 | ||
|
|
25bf7a3747 | ||
|
|
f8f0a23ede | ||
|
|
b4d7ed59de | ||
|
|
f64d07c1e8 | ||
|
|
4dc31a4635 | ||
|
|
cb243e1c04 | ||
|
|
bb5bdfe0c7 | ||
|
|
be144df5de | ||
|
|
a6f68810fc | ||
|
|
3ac01c9f1b | ||
|
|
c9d496e251 | ||
|
|
be31fdeb0a | ||
|
|
1ced445742 | ||
|
|
e52fffbe75 | ||
|
|
25b4cfe3f2 | ||
|
|
bc8453a96e | ||
|
|
0379b0c62c | ||
|
|
cdf34b76b8 | ||
|
|
d3cc9eb989 | ||
|
|
3048483b0c | ||
|
|
f16a5800e5 | ||
|
|
672f1f0ff3 | ||
|
|
a3ddfbefe3 | ||
|
|
e548a9a94b | ||
|
|
80d3069ffb | ||
|
|
fad9824e4d | ||
|
|
2bb7e3023d | ||
|
|
105f76d8f4 | ||
|
|
8e7874b691 | ||
|
|
710a69543a | ||
|
|
8b180342d0 | ||
|
|
1a62c596cb | ||
|
|
2b8e2c90c3 | ||
|
|
dcb9c97d0a | ||
|
|
74ed8783a8 | ||
|
|
0d07e6aed3 | ||
|
|
49fa7a6ff7 | ||
|
|
f40df14d03 | ||
|
|
6bc63e3fde | ||
|
|
c0b6e8ff40 | ||
|
|
0a4a1ce0cf | ||
|
|
35fd75aebb | ||
|
|
59e8dc057a | ||
|
|
91cd767dd7 | ||
|
|
57ca1ff88c | ||
|
|
15dd00fdcc | ||
|
|
10b14de218 | ||
|
|
66899564e8 | ||
|
|
6e8d8319f7 | ||
|
|
3ad8be9d3e | ||
|
|
b7c175060d | ||
|
|
ba6043e654 | ||
|
|
4bfd3339a2 | ||
|
|
c03d8d45e0 | ||
|
|
b150db8047 | ||
|
|
197ff4f813 | ||
|
|
fc6a5f8747 | ||
|
|
a4a37b39e5 | ||
|
|
b057b88e26 | ||
|
|
2dd383a640 | ||
|
|
d6526ae64e | ||
|
|
8be8b831ba | ||
|
|
ff1ded1fda | ||
|
|
bcc77cf3e0 | ||
|
|
4213d0824f | ||
|
|
729722259a | ||
|
|
6c408d823a | ||
|
|
9b7ec9d8bf | ||
|
|
3cce54a62d | ||
|
|
40119254a7 | ||
|
|
4b33e69d11 | ||
|
|
90d08d651e | ||
|
|
11b73a05c8 | ||
|
|
b68c8dd0d8 | ||
|
|
8933caa51f | ||
|
|
e82b1f760a | ||
|
|
dc97079967 | ||
|
|
2e44decaa9 | ||
|
|
244f7efe21 | ||
|
|
d299db678d | ||
|
|
0e558af1f1 | ||
|
|
a6cd5978e3 | ||
|
|
24c893bb1d | ||
|
|
8a80446c8c | ||
|
|
a88cc02f9a | ||
|
|
df442063ea | ||
|
|
40c49ba3ee | ||
|
|
9135f817d1 | ||
|
|
07fc9894b1 | ||
|
|
930612b5bb | ||
|
|
29a87c831a | ||
|
|
722340140a | ||
|
|
96d4834f83 | ||
|
|
308b42948a | ||
|
|
8e304bb09d | ||
|
|
7f2c23e69e | ||
|
|
1d68c4fee0 | ||
|
|
3d808ea718 | ||
|
|
79f1933b9a | ||
|
|
4089e75f2d | ||
|
|
a2d4e12ca6 | ||
|
|
72e5d596fc | ||
|
|
4f2dea5036 | ||
|
|
915dc25284 | ||
|
|
13e4468e93 | ||
|
|
ca23f0d546 | ||
|
|
53c450170a | ||
|
|
348521cc3e | ||
|
|
f4ce887d24 | ||
|
|
5e176dc10e | ||
|
|
00cd9a8bb1 | ||
|
|
0166aeadb2 | ||
|
|
864c22c3ed | ||
|
|
910b88d663 | ||
|
|
4a56c28a0d | ||
|
|
1c13465bf4 | ||
|
|
f4d46f03aa | ||
|
|
5b3e3ef179 | ||
|
|
392b12e592 | ||
|
|
e99f7772e6 | ||
|
|
54d20e9e50 | ||
|
|
fd0e361474 | ||
|
|
551e38f882 | ||
|
|
d18f04c010 | ||
|
|
3b7f3043f1 | ||
|
|
faf2a8d083 | ||
|
|
42071e3bad | ||
|
|
978d32e947 | ||
|
|
fe670cb3e2 | ||
|
|
d2f4a7d9a5 | ||
|
|
b162ce1825 | ||
|
|
fbdcce3c3b | ||
|
|
2d939f095c | ||
|
|
034e2f2b98 | ||
|
|
f429ab0a60 | ||
|
|
5578d0879b | ||
|
|
8b1e6244ea | ||
|
|
49c7b9c775 | ||
|
|
da1c724464 | ||
|
|
de15150a04 | ||
|
|
0d1fb2b260 | ||
|
|
55d0779a33 | ||
|
|
8f41d464e1 | ||
|
|
1db2b6c934 | ||
|
|
aa58f76057 | ||
|
|
1def0a1061 | ||
|
|
3b4a66b04e | ||
|
|
7384b2e55c | ||
|
|
f8eaed445a | ||
|
|
dfef9190ca | ||
|
|
7ce61834c2 | ||
|
|
240a7ab242 | ||
|
|
b3de59bfba | ||
|
|
5d5de39845 | ||
|
|
59af774d09 | ||
|
|
18828207c0 | ||
|
|
5c67922996 | ||
|
|
af50a45e65 | ||
|
|
7ccd265a30 | ||
|
|
e76f22f071 | ||
|
|
3c03b719d2 | ||
|
|
cd71531766 | ||
|
|
282c116d02 | ||
|
|
820171916b | ||
|
|
136fbb0ba8 | ||
|
|
4f0c05c357 | ||
|
|
0377838a26 | ||
|
|
b72ff1b772 | ||
|
|
287488f1b6 | ||
|
|
9ffb67db1f | ||
|
|
a563c0a8a8 | ||
|
|
234086444f | ||
|
|
a93b0834a2 | ||
|
|
b5550dfe82 | ||
|
|
d99300ecac | ||
|
|
1c0688a172 | ||
|
|
ff3b117189 | ||
|
|
aaed1b32cd | ||
|
|
38c8e1d2c3 | ||
|
|
1e1792d132 | ||
|
|
b29462a5cb | ||
|
|
c9161ebe24 | ||
|
|
8441c6a8bf | ||
|
|
72c2ba77ee | ||
|
|
a097790b76 | ||
|
|
b7129e8aa8 | ||
|
|
ab9c2b8be1 | ||
|
|
a9a37b8fc8 | ||
|
|
d50cb67a26 | ||
|
|
5e42500308 | ||
|
|
50997a847f | ||
|
|
2305d85c01 | ||
|
|
33bbc35d88 | ||
|
|
28b3e100fb | ||
|
|
edae2d699b | ||
|
|
0cc477a769 | ||
|
|
53b84a999f | ||
|
|
49487c11ff | ||
|
|
0c4070b7a7 | ||
|
|
67909a4862 | ||
|
|
28ee563083 | ||
|
|
28b2b528c1 | ||
|
|
a53d9d0850 | ||
|
|
873a8812aa | ||
|
|
19ebc90963 | ||
|
|
dfbd1381de | ||
|
|
c9fe48a067 | ||
|
|
49b022b665 | ||
|
|
86be23ae5f | ||
|
|
420412978e | ||
|
|
25e2f4dae2 | ||
|
|
d767be96b5 | ||
|
|
6d73acca9b | ||
|
|
1f116374ad | ||
|
|
d0b7d96208 | ||
|
|
16285d7064 | ||
|
|
1f27084fe1 | ||
|
|
261aace0db | ||
|
|
344b910214 | ||
|
|
fbaa4896e1 | ||
|
|
2f93064f60 | ||
|
|
65b47c5e07 | ||
|
|
d70d01f2d3 | ||
|
|
769d7d3bcd | ||
|
|
a5f47688e0 | ||
|
|
7d2e6a25a4 | ||
|
|
69999b3b7a | ||
|
|
94d1bd4eb9 | ||
|
|
c18e8b7ab7 | ||
|
|
9f45187300 | ||
|
|
0c6ba8a9b7 | ||
|
|
1af69f2e8b | ||
|
|
a3b836b1c5 | ||
|
|
1eca6c81ad | ||
|
|
b9b4a78b5c | ||
|
|
fda23c5539 | ||
|
|
8297321971 | ||
|
|
cfeaf00f26 | ||
|
|
5ee1b32249 | ||
|
|
b69f0dc90e | ||
|
|
fe6cae4e47 | ||
|
|
b494b5c23c | ||
|
|
3bd3395128 | ||
|
|
3e3f63220d | ||
|
|
ccba0f042f | ||
|
|
abdd89259d | ||
|
|
256f203c48 | ||
|
|
205412b02c | ||
|
|
990289feb2 | ||
|
|
44b1ff1a1f | ||
|
|
f9e11bb9e5 | ||
|
|
eef1584b1c | ||
|
|
937dd84d1c | ||
|
|
80b9fe280b | ||
|
|
3892892792 | ||
|
|
519551e76c | ||
|
|
b30476ae3f | ||
|
|
9d417a12f1 | ||
|
|
fd5406b95d | ||
|
|
0ef59e557d | ||
|
|
15ed1db347 | ||
|
|
429d5b90d4 | ||
|
|
b3ab931aca | ||
|
|
21b42d0c72 | ||
|
|
7b1e1a27ba | ||
|
|
fb7b1e791f | ||
|
|
eecd59b387 | ||
|
|
ff7d8e0293 | ||
|
|
19c6d77a2c | ||
|
|
e002aa34e9 | ||
|
|
865e53a4a8 | ||
|
|
b8c652b7fb | ||
|
|
c4080f82dc | ||
|
|
1a33497e8f | ||
|
|
f8353b8eda | ||
|
|
4980ad54ec | ||
|
|
5363305411 | ||
|
|
6ff47619ad | ||
|
|
69315a3f74 | ||
|
|
8b1ddba672 | ||
|
|
bf63f0e209 | ||
|
|
450c923d24 | ||
|
|
9a922a6df5 | ||
|
|
08a5a9a021 | ||
|
|
61ad7d8533 | ||
|
|
52d2b3c096 | ||
|
|
b0a69cc048 | ||
|
|
74e445a62e | ||
|
|
91219202b2 | ||
|
|
a3545c83a0 | ||
|
|
31baf91e8f | ||
|
|
214cd53751 | ||
|
|
2be16882c7 | ||
|
|
75e2264b44 | ||
|
|
fd7a0eb6a5 | ||
|
|
177115f144 | ||
|
|
edbe31dc9c | ||
|
|
21a2f9ef2f |
No files matched your search
@@ -1 +0,0 @@
|
||||
.gitea/CODEOWNERS linguist-detectable=false
|
||||
@@ -1,3 +0,0 @@
|
||||
# Pull requests request review from the eligible staff team(s).
|
||||
# One approval from either an Admin or Developer satisfies branch protection.
|
||||
* @trueog/Admins @trueog/Developers
|
||||
+49
-3
@@ -1,6 +1,52 @@
|
||||
# Maintenance Changelog
|
||||
This file contains update logs for this project. The top may contain a `Unreleased` section, gathering update logs for a future update during development.
|
||||
|
||||
## 5.1.1 - October 17th 2026
|
||||
|
||||
### Changed
|
||||
- Velocity/Bungee: Timer commands now accept `global` as the server argument, with an optional mode: `/maintenance starttimer global [mode] <time>`, `/maintenance scheduletimer global [mode] <time> <duration>`, `/maintenance endtimer global <time>`
|
||||
- Timer durations now also accept days, e.g. `2d` or `1d12h`
|
||||
- Folia: Now supports timers
|
||||
|
||||
### Fixed
|
||||
- Folia: Fixed the update check stalling the global region thread
|
||||
- Velocity/Bungee: Fixed the `redis.enabled` confg key
|
||||
- Velocity/Bungee: `/maintenance reload` now keeps the maintenance status from Redis instead of overriding it with the local config
|
||||
- Velocity/Bungee: Fixed `/maintenance aborttimer` tab completion not suggesting servers with running timers
|
||||
|
||||
---
|
||||
## 5.1.0 - May 17th 2026
|
||||
|
||||
### Changed
|
||||
- Made Discord webhooks configurable separately from command messages, including a new `%TIMESTAMP%` parameter for Discord timestamps (e.g. `<t:%TIMESTAMP%:R>`)
|
||||
|
||||
### Fixed
|
||||
- Velocity/Bungee: Fixed proxied-maintenance-servers config loading
|
||||
---
|
||||
## 5.0.0 - April 17th 2026
|
||||
|
||||
This update removes *a lot* of legacy code, making the code-base a great bit more simple and a lot more performant.
|
||||
|
||||
### Requirements
|
||||
- The Paper part now **requires at least Paper 1.20.1** and no longer supports Spigot
|
||||
- The universal jar has been removed. Each platform now has its own jar (that is as small as possible)
|
||||
- Maintenance now **requires at least Java 21**
|
||||
|
||||
### Changed
|
||||
- **Added mode-based maintenance messages** for `ping-message`, `player-count-message`, and `player-list-hover-message`
|
||||
- You can have different MOTDs depending on the pre-configured mode, e.g. a generic `default` or one for `upcoming-release`.
|
||||
- Updated proxy/global toggle syntax: `/maintenance on [mode]`
|
||||
- For proxy servers: `/maintenance on global [mode]`, `/maintenance on <server> [mode]`
|
||||
- Other updated commands: `/maintenance motd [timer] [mode]`, `/maintenance setmotd [timer] [mode] ...`, `/maintenance removemotd [timer] [mode] ...`
|
||||
- **Added `webhook` config option for Discord webhooks** when the maintenance status changes, a timer is started or a join is blocked
|
||||
- **Added `%REASON%` variable to messages**, using the `active-reason` config option. This allows for dynamic MOTD and other messages via a quick command without modifying the messages themselves
|
||||
- Can be changed via the `/maintenance setreason <reason>` command
|
||||
- **Added `/maintenance status` command** on Paper/Sponge to display the current maintenance status as well as any running start or end timers.
|
||||
- Velocity/Bungee: **The existing timer command now displays start and end timers** for proxied servers.
|
||||
- Velocity/Bungee: **Replaced `mysql` section with `redis`**. This is a more suitable database for such reading and sharing of data and no longer means some server pings can take multiple seconds, if you have multiple proxies linked
|
||||
|
||||
### Fixed
|
||||
- Velocity/Bungee: Fixed the `/maintenance schedule` command for single servers having swapped time arguments
|
||||
---
|
||||
## 4.3.0 - September 17th 2024
|
||||
### Changed
|
||||
@@ -64,8 +110,8 @@ This file contains update logs for this project. The top may contain a `Unreleas
|
||||
### Changed
|
||||
* Added support for hex colors in messages and replaced the component parsing with adventure-minimessage
|
||||
* This means that section symbols (`§`) are no longer the intended/supported format
|
||||
* You can use the following page to edit and preview formatted text: https://webui.adventure.kyori.net/
|
||||
* You can find full documentation on the format (including normal colors and formatting, rgb, click/hover events, and more) here: https://docs.adventure.kyori.net/minimessage/format
|
||||
* You can use the following page to edit and preview formatted text: https://webui.advntr.dev/
|
||||
* You can find full documentation on the format (including normal colors and formatting, rgb, click/hover events, and more) here: https://docs.papermc.io/adventure/minimessage/format/
|
||||
* Your config and language file will be updated to the new format automatically, you won't have to change anything manually!
|
||||
* Note that the player count and player count hover messages do not support rgb colors
|
||||
* Updated Sponge module to SpongeAPI v8 (Minecraft 1.15+)
|
||||
@@ -327,4 +373,4 @@ This file contains update logs for this project. The top may contain a `Unreleas
|
||||
### Fixed
|
||||
* Fixed some permission and message bugs
|
||||
* Fixed problems with adding and removing players from the maintenance whitelist
|
||||
* Fixed update checks
|
||||
* Fixed update checks
|
||||
@@ -0,0 +1,14 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
- package-ecosystem: "gradle"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
@@ -7,20 +7,22 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: gradle/actions/wrapper-validation@v3
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v6
|
||||
with:
|
||||
java-version: 17
|
||||
java-version: 21
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
|
||||
- name: Upload a Build Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: Maintenance Files
|
||||
path: build/libs
|
||||
@@ -14,7 +14,7 @@ Its features include:
|
||||
* Nearly all messages are editable via the language file, given in a multitude of different languages
|
||||
* Features specifically for Bungee/Velocity
|
||||
* Only want to enable maintenance on a single server? You can also do so by using the `/maintenance <on/off> <servername>` command
|
||||
* Link multiple proxy instances through a MySQL database connection, so you don't have to change maintenance on each proxy by hand
|
||||
* Link multiple proxy instances through a Redis connection, so you don't have to change maintenance status or whitelisted players on each proxy by hand
|
||||
* Use PlaceholderAPI placeholders with the [**MaintenanceAddon**](https://hangar.papermc.io/kennytv/MaintenanceAddon) (for Velocity and Bungee)
|
||||
|
||||
A full list of commands, permissions and configuration options can be found in the wiki listed below.
|
||||
@@ -33,7 +33,7 @@ Join [our Discord](https://discord.gg/vGCUzHq) if you want to get the proofreade
|
||||
Adding translations for languages not yet listed is always appreciated as well!
|
||||
|
||||
## Compiling
|
||||
To create a working jar yourself, simply clone the project and compile it with Gradle (`gradlew build`).
|
||||
To create a working jar yourself, simply clone the project and compile it with Gradle (`gradlew build`). To compile, you need a JDK 21+ (although it targets 17).
|
||||
|
||||
## License
|
||||
This project is licensed under the [GNU General Public License v3](LICENSE.txt).
|
||||
@@ -1,24 +0,0 @@
|
||||
plugins {
|
||||
id("com.gradleup.shadow")
|
||||
}
|
||||
|
||||
// Shade and relocate adventure in an extra module, so that common/the rest can directly depend on a
|
||||
// relocated adventure without breaking native platform's adventure usage with project wide relocation
|
||||
tasks {
|
||||
shadowJar {
|
||||
relocate("net.kyori", "eu.kennytv.maintenance.lib.kyori")
|
||||
}
|
||||
build {
|
||||
dependsOn(shadowJar)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(libs.bundles.adventure) {
|
||||
exclude("net.kyori", "adventure-api")
|
||||
exclude("net.kyori", "adventure-bom")
|
||||
exclude("com.google.code.gson", "gson")
|
||||
}
|
||||
}
|
||||
|
||||
publishShadowJar()
|
||||
@@ -1,20 +0,0 @@
|
||||
plugins {
|
||||
id("com.gradleup.shadow")
|
||||
}
|
||||
|
||||
tasks {
|
||||
shadowJar {
|
||||
relocate("net.kyori", "eu.kennytv.maintenance.lib.kyori")
|
||||
}
|
||||
build {
|
||||
dependsOn(shadowJar)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(libs.adventurePlatformBukkit) {
|
||||
exclude("net.kyori", "adventure-api")
|
||||
}
|
||||
}
|
||||
|
||||
publishShadowJar()
|
||||
@@ -1,20 +0,0 @@
|
||||
plugins {
|
||||
id("com.gradleup.shadow")
|
||||
}
|
||||
|
||||
tasks {
|
||||
shadowJar {
|
||||
relocate("net.kyori", "eu.kennytv.maintenance.lib.kyori")
|
||||
}
|
||||
build {
|
||||
dependsOn(shadowJar)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(libs.adventurePlatformBungee) {
|
||||
exclude("net.kyori", "adventure-api")
|
||||
}
|
||||
}
|
||||
|
||||
publishShadowJar()
|
||||
@@ -4,9 +4,7 @@ plugins {
|
||||
|
||||
dependencies {
|
||||
api(projects.maintenanceApi)
|
||||
compileOnly(projects.adventure.adventureApi) {
|
||||
targetConfiguration = "shadow"
|
||||
}
|
||||
compileOnly(libs.adventureApi)
|
||||
}
|
||||
|
||||
java {
|
||||
|
||||
+3
-9
@@ -15,17 +15,11 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.proxy.server;
|
||||
package eu.kennytv.maintenance.api.proxy;
|
||||
|
||||
import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public final class DummyServer implements Server {
|
||||
private final String name;
|
||||
|
||||
public DummyServer(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public record DummyServer(String name) implements Server {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
@@ -18,23 +18,26 @@
|
||||
package eu.kennytv.maintenance.api.proxy;
|
||||
|
||||
import eu.kennytv.maintenance.api.Maintenance;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface MaintenanceProxy extends Maintenance {
|
||||
|
||||
/**
|
||||
* Enables/disables maintenance mode on a proxied server.
|
||||
* If enabled, all non-permitted players will be kicked.
|
||||
* If MySQL is enabled, it will also be written into the database.
|
||||
* If Redis is enabled, it will also be written into the database.
|
||||
*
|
||||
* @param server server to apply the maintenance status to
|
||||
* @param maintenance true to enable, false to disable maintenance mode
|
||||
* @return true if the mode was changed
|
||||
* @see #getServer(String)
|
||||
*/
|
||||
boolean setMaintenanceToServer(Server server, boolean maintenance);
|
||||
boolean setMaintenanceToServer(Server server, boolean maintenance, @Nullable String mode);
|
||||
|
||||
default boolean setMaintenanceToServer(final Server server, final boolean maintenance) {
|
||||
return setMaintenanceToServer(server, maintenance, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param server server to check
|
||||
@@ -59,6 +62,18 @@ public interface MaintenanceProxy extends Maintenance {
|
||||
@Nullable
|
||||
Server getServer(String server);
|
||||
|
||||
/**
|
||||
* Returns a wrapped {@link Server} object of a proxied server, or a dummy server if not registered.
|
||||
*
|
||||
* @param serverName name of the proxied server
|
||||
* @return wrapped server object, or a dummy if not registered
|
||||
* @see Server#isRegisteredServer()
|
||||
*/
|
||||
default Server getServerOrDummy(final String serverName) {
|
||||
final Server server = getServer(serverName);
|
||||
return server != null ? server : new DummyServer(serverName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently registered proxied servers.
|
||||
*
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
package eu.kennytv.maintenance.api.proxy;
|
||||
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
/**
|
||||
* Wrapper for a proxied server.
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package eu.kennytv.maintenance.api;
|
||||
|
||||
import eu.kennytv.maintenance.api.event.manager.EventManager;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface Maintenance {
|
||||
|
||||
@@ -25,13 +26,18 @@ public interface Maintenance {
|
||||
* Enables/disables maintenance mode.
|
||||
* If enabled, all non-permitted players will be kicked.
|
||||
* <p>
|
||||
* If using the BungeeCord version and MySQL is enabled,
|
||||
* If using the BungeeCord version and Redis is enabled,
|
||||
* it will also be written into the database.
|
||||
* </p>
|
||||
*
|
||||
* @param maintenance true to enable, false to disable maintenance mode
|
||||
* @param mode mode, used for custom messages
|
||||
*/
|
||||
void setMaintenance(boolean maintenance);
|
||||
void setMaintenance(boolean maintenance, @Nullable String mode);
|
||||
|
||||
default void setMaintenance(boolean maintenance) {
|
||||
setMaintenance(maintenance, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if maintenance is currently enabled
|
||||
|
||||
@@ -19,16 +19,39 @@ package eu.kennytv.maintenance.api;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface Settings {
|
||||
|
||||
/**
|
||||
* Note that the value might need a few seconds to update, if you're on BungeeCord with MySQL enabled
|
||||
* Returns whether maintenance is currently enabled.
|
||||
*
|
||||
* @return true if maintenance is currently enabled
|
||||
*/
|
||||
boolean isMaintenance();
|
||||
|
||||
/**
|
||||
* Returns the current active maintenance mode, or null is set to default.
|
||||
* Used when getting MOTD/player count messages.
|
||||
*
|
||||
* @return the current active maintenance mode, or null if set to default
|
||||
*/
|
||||
@Nullable String activeMode();
|
||||
|
||||
/**
|
||||
* Returns the reason for the current active mode.
|
||||
*
|
||||
* @return variable reason for the current active mode, or empty if no reason is set
|
||||
*/
|
||||
String activeReason();
|
||||
|
||||
/**
|
||||
* Sets the reason for the current active mode, used in %REASON% message placeholders.
|
||||
*
|
||||
* @param reason reason to set for the current active mode, or null to remove the reason
|
||||
*/
|
||||
void setActiveReason(@Nullable String reason);
|
||||
|
||||
/**
|
||||
* @return true if custom ping messages during maintenance should be used
|
||||
*/
|
||||
@@ -73,8 +96,8 @@ public interface Settings {
|
||||
*
|
||||
* @param name name of the player to remove with original lower-/uppercase letters
|
||||
* @return true if removing the player was successful
|
||||
* @deprecated if a player changes their name, the list will still contain the old one
|
||||
* @see #removeWhitelistedPlayer(UUID)
|
||||
* @deprecated if a player changes their name, the list will still contain the old one
|
||||
*/
|
||||
@Deprecated
|
||||
boolean removeWhitelistedPlayer(String name);
|
||||
|
||||
@@ -22,17 +22,5 @@ import eu.kennytv.maintenance.api.event.manager.MaintenanceEvent;
|
||||
/**
|
||||
* Notification event fired when maintenance mode has been changed.
|
||||
*/
|
||||
public final class MaintenanceChangedEvent implements MaintenanceEvent {
|
||||
private final boolean maintenance;
|
||||
|
||||
public MaintenanceChangedEvent(final boolean maintenance) {
|
||||
this.maintenance = maintenance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if maintenance has been enabled, false otherwise
|
||||
*/
|
||||
public boolean isMaintenance() {
|
||||
return maintenance;
|
||||
}
|
||||
public record MaintenanceChangedEvent(boolean isMaintenance) implements MaintenanceEvent {
|
||||
}
|
||||
@@ -8,5 +8,6 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
// version must be manually kept in sync with the one in root project settings.gradle.kts
|
||||
implementation("com.gradleup.shadow", "shadow-gradle-plugin", "8.3.9")
|
||||
implementation("com.gradleup.shadow:shadow-gradle-plugin:9.6.1")
|
||||
implementation("com.gradleup.nmcp:com.gradleup.nmcp.gradle.plugin:1.6.2")
|
||||
}
|
||||
@@ -6,12 +6,12 @@ import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.gradle.kotlin.dsl.named
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
fun Project.publishShadowJar() {
|
||||
configurePublication {
|
||||
artifact(tasks["shadowJar"])
|
||||
artifact(tasks["sourcesJar"])
|
||||
artifact(tasks["javadocJar"])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,3 @@ fun Project.latestCommitHash(): String {
|
||||
commandLine = listOf("git", "rev-parse", "--short", "HEAD")
|
||||
}.standardOutput.asBytes.get().toString(Charsets.UTF_8).trim()
|
||||
}
|
||||
|
||||
fun JavaPluginExtension.javaTarget(version: Int) {
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
@@ -2,6 +2,7 @@ plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
signing
|
||||
id("com.gradleup.nmcp")
|
||||
}
|
||||
|
||||
tasks {
|
||||
@@ -10,7 +11,7 @@ tasks {
|
||||
val ver = project.version.toString()
|
||||
val desc = project.description
|
||||
filesMatching(listOf("plugin.yml", "bungee.yml", "META-INF/sponge_plugins.json")) {
|
||||
expand("version" to ver, "description" to desc)
|
||||
expand(mapOf("version" to ver, "description" to desc))
|
||||
}
|
||||
}
|
||||
javadoc {
|
||||
@@ -27,8 +28,10 @@ tasks {
|
||||
}
|
||||
|
||||
java {
|
||||
javaTarget(8)
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
signing {
|
||||
@@ -45,6 +48,11 @@ publishing {
|
||||
name.set("Maintenance")
|
||||
description.set("Paper plugin to enable maintenance mode on your Minecraft server.")
|
||||
url.set("https://github.com/kennytv/Maintenance")
|
||||
scm {
|
||||
url.set("https://github.com/kennytv/Maintenance")
|
||||
connection.set("scm:git:https://github.com/kennytv/Maintenance.git")
|
||||
developerConnection.set("scm:git:ssh://git@github.com/kennytv/Maintenance.git")
|
||||
}
|
||||
licenses {
|
||||
license {
|
||||
name.set("GNU GPLv3")
|
||||
@@ -59,15 +67,4 @@ publishing {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!name.startsWith("adventure")) {
|
||||
repositories.maven {
|
||||
name = "OSSRH"
|
||||
url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
|
||||
credentials {
|
||||
username = System.getenv("OSSRH_USERNAME")
|
||||
password = System.getenv("OSSRH_PASSWORD")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
import org.gradle.kotlin.dsl.named
|
||||
|
||||
plugins {
|
||||
id("maintenance.base-conventions")
|
||||
@@ -24,6 +23,10 @@ tasks {
|
||||
publishShadowJar()
|
||||
|
||||
fun ShadowJar.configureRelocations() {
|
||||
relocate("com.zaxxer.hikari", "eu.kennytv.maintenance.lib.hikari")
|
||||
relocate("org.bstats", "eu.kennytv.maintenance.lib.bstats")
|
||||
|
||||
// Proxy
|
||||
relocate("redis.client", "eu.kennytv.maintenance.lib.redis.client")
|
||||
relocate("org.json", "eu.kennytv.maintenance.lib.json")
|
||||
relocate("org.apache.commons.pool2", "eu.kennytv.maintenance.lib.apache.commons.pool2")
|
||||
}
|
||||
+21
-6
@@ -1,9 +1,10 @@
|
||||
import org.gradle.api.plugins.JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME
|
||||
import org.gradle.api.plugins.JavaPlugin.TEST_RUNTIME_ONLY_CONFIGURATION_NAME
|
||||
|
||||
plugins {
|
||||
base
|
||||
id("eclipse")
|
||||
id("maintenance.build-logic")
|
||||
id("com.gradleup.nmcp.aggregation") version "1.6.2"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
@@ -14,17 +15,14 @@ allprojects {
|
||||
|
||||
val platforms = setOf(
|
||||
projects.maintenancePaper,
|
||||
projects.maintenanceBungee,
|
||||
projects.maintenanceSponge,
|
||||
projects.maintenanceVelocity
|
||||
).map { it.path }
|
||||
|
||||
val special = setOf(
|
||||
projects.maintenance,
|
||||
projects.maintenanceApi,
|
||||
projects.maintenanceApiProxy,
|
||||
projects.adventure,
|
||||
projects.adventure.adventureApi,
|
||||
projects.adventure.adventurePlatformBukkit,
|
||||
projects.maintenanceApiProxy
|
||||
).map { it.path }
|
||||
|
||||
subprojects {
|
||||
@@ -37,5 +35,22 @@ subprojects {
|
||||
dependencies {
|
||||
TEST_IMPLEMENTATION_CONFIGURATION_NAME(rootProject.libs.bundles.junit)
|
||||
TEST_IMPLEMENTATION_CONFIGURATION_NAME(rootProject.libs.snakeyaml)
|
||||
TEST_RUNTIME_ONLY_CONFIGURATION_NAME("org.junit.platform:junit-platform-launcher")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
allprojects {
|
||||
nmcpAggregation(project(path))
|
||||
}
|
||||
}
|
||||
|
||||
nmcpAggregation {
|
||||
centralPortal {
|
||||
username = System.getenv("MAVEN_CENTRAL_USERNAME")
|
||||
password = System.getenv("MAVEN_CENTRAL_PASSWORD")
|
||||
publishingType = "USER_MANAGED"
|
||||
publicationName = "maintenance:$version"
|
||||
publishingTimeout = java.time.Duration.ZERO
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
dependencies {
|
||||
implementation(projects.maintenanceCoreProxy)
|
||||
implementation(libs.bstatsBungee)
|
||||
compileOnly(libs.bungee)
|
||||
}
|
||||
|
||||
tasks {
|
||||
shadowJar {
|
||||
relocate("net.kyori", "eu.kennytv.maintenance.lib.kyori")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(libs.bundles.adventureBungee) {
|
||||
exclude("com.google.code.gson", "gson")
|
||||
exclude("org.slf4j", "slf4j-api")
|
||||
exclude("net.kyori", "adventure-bom")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.bungee;
|
||||
|
||||
import eu.kennytv.maintenance.api.Maintenance;
|
||||
import eu.kennytv.maintenance.core.MaintenanceBase;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public final class MaintenanceBungeeBase extends Plugin implements MaintenanceBase {
|
||||
private MaintenanceBungeePlugin maintenance;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
maintenance = new MaintenanceBungeePlugin(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
maintenance.disable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Maintenance getApi() {
|
||||
return maintenance;
|
||||
}
|
||||
|
||||
File getPluginFile() {
|
||||
return getFile();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.bungee;
|
||||
|
||||
import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.bungee.command.MaintenanceBungeeCommand;
|
||||
import eu.kennytv.maintenance.bungee.command.MaintenanceBungeeCommandBase;
|
||||
import eu.kennytv.maintenance.bungee.listener.ProxyPingListener;
|
||||
import eu.kennytv.maintenance.bungee.listener.ServerConnectListener;
|
||||
import eu.kennytv.maintenance.bungee.util.BungeeSenderInfo;
|
||||
import eu.kennytv.maintenance.bungee.util.BungeeServer;
|
||||
import eu.kennytv.maintenance.bungee.util.BungeeTask;
|
||||
import eu.kennytv.maintenance.bungee.util.ComponentUtil;
|
||||
import eu.kennytv.maintenance.core.dump.PluginDump;
|
||||
import eu.kennytv.maintenance.core.hook.ServerListPlusHook;
|
||||
import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.SettingsProxy;
|
||||
import eu.kennytv.maintenance.core.proxy.hook.LuckPermsProxyHook;
|
||||
import eu.kennytv.maintenance.core.proxy.util.ProfileLookup;
|
||||
import eu.kennytv.maintenance.core.proxy.util.ProxyOfflineSenderInfo;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.core.util.ServerType;
|
||||
import eu.kennytv.maintenance.core.util.Task;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.imageio.ImageIO;
|
||||
import net.kyori.adventure.platform.bungeecord.BungeeAudiences;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.Favicon;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
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 net.md_5.bungee.api.plugin.PluginManager;
|
||||
import org.bstats.bungeecord.Metrics;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class MaintenanceBungeePlugin extends MaintenanceProxyPlugin {
|
||||
private final MaintenanceBungeeBase plugin;
|
||||
private final BungeeAudiences audiences;
|
||||
private Favicon favicon;
|
||||
|
||||
MaintenanceBungeePlugin(final MaintenanceBungeeBase plugin) {
|
||||
super(plugin.getDescription().getVersion(), ServerType.BUNGEE);
|
||||
this.plugin = plugin;
|
||||
this.audiences = BungeeAudiences.create(plugin);
|
||||
|
||||
settingsProxy = new SettingsProxy(this);
|
||||
settings = settingsProxy;
|
||||
|
||||
sendEnableMessage();
|
||||
|
||||
final PluginManager pm = getProxy().getPluginManager();
|
||||
pm.registerListener(plugin, new ProxyPingListener(this, settingsProxy));
|
||||
pm.registerListener(plugin, new ServerConnectListener(this, settingsProxy));
|
||||
commandManager = new MaintenanceBungeeCommand(this, settingsProxy);
|
||||
pm.registerCommand(plugin, new MaintenanceBungeeCommandBase(commandManager));
|
||||
|
||||
continueLastEndtimer();
|
||||
new Metrics(plugin, 742);
|
||||
|
||||
final Plugin serverListPlus = pm.getPlugin("ServerListPlus");
|
||||
if (serverListPlus != null) {
|
||||
serverListPlusHook = new ServerListPlusHook(serverListPlus);
|
||||
if (settings.isEnablePingMessages()) {
|
||||
serverListPlusHook.setEnabled(!settingsProxy.isMaintenance());
|
||||
}
|
||||
plugin.getLogger().info("Enabled ServerListPlus integration!");
|
||||
}
|
||||
|
||||
if (false && pm.getPlugin("LuckPerms") != null) {
|
||||
LuckPermsProxyHook.<ProxiedPlayer>register(this, player -> {
|
||||
final net.md_5.bungee.api.connection.Server server = player.getServer();
|
||||
return server != null ? server.getInfo().getName() : null;
|
||||
});
|
||||
getLogger().info("Registered LuckPerms context");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isMaintenance(final ServerInfo serverInfo) {
|
||||
return settingsProxy.isMaintenance(serverInfo.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void kickPlayersFromProxy() {
|
||||
for (final ProxiedPlayer player : getProxy().getPlayers()) {
|
||||
if (!hasPermission(player, "bypass") && !settingsProxy.isWhitelisted(player.getUniqueId())) {
|
||||
player.disconnect(ComponentUtil.toBadComponents(settingsProxy.getKickMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void kickPlayers(final Server server, final Server fallback) {
|
||||
// Kick players from a proxied server
|
||||
final ServerInfo fallbackServer = fallback != null ? ((BungeeServer) fallback).server() : null;
|
||||
final boolean checkForFallback = fallbackServer != null && !isMaintenance(fallback);
|
||||
for (final ProxiedPlayer player : ((BungeeServer) server).server().getPlayers()) {
|
||||
if (!hasPermission(player, "bypass") && !settingsProxy.isWhitelisted(player.getUniqueId())) {
|
||||
if (checkForFallback && fallbackServer.canAccess(player)) {
|
||||
audiences.player(player).sendMessage(settingsProxy.getMessage("singleMaintenanceActivated", "%SERVER%", server.getName()));
|
||||
player.connect(fallbackServer);
|
||||
} else {
|
||||
player.disconnect(ComponentUtil.toBadComponents(settingsProxy.getFullServerKickMessage(server.getName())));
|
||||
}
|
||||
} else {
|
||||
audiences.player(player).sendMessage(settingsProxy.getMessage("singleMaintenanceActivated", "%SERVER%", server.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void kickPlayersTo(final Server server) {
|
||||
// Kick all players to a single waiting server
|
||||
final ServerInfo serverInfo = ((BungeeServer) server).server();
|
||||
// Notifications done in global method
|
||||
for (final ProxiedPlayer player : getProxy().getPlayers()) {
|
||||
if (hasPermission(player, "bypass") || settingsProxy.isWhitelisted(player.getUniqueId())) continue;
|
||||
if (player.getServer() != null && player.getServer().getInfo().getName().equals(serverInfo.getName()))
|
||||
continue;
|
||||
if (serverInfo.canAccess(player) && !isMaintenance(serverInfo)) {
|
||||
audiences.player(player).sendMessage(settingsProxy.getMessage("sentToWaitingServer", "%SERVER%", server.getName()));
|
||||
player.connect(serverInfo);
|
||||
} else {
|
||||
player.disconnect(ComponentUtil.toBadComponents(settingsProxy.getKickMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Task startMaintenanceRunnable(final Runnable runnable) {
|
||||
return new BungeeTask(getProxy().getScheduler().schedule(plugin, runnable, 0, 1, TimeUnit.SECONDS).getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<@Nullable SenderInfo> getOfflinePlayer(final String name) {
|
||||
final ProxiedPlayer player = getProxy().getPlayer(name);
|
||||
if (player != null) {
|
||||
return CompletableFuture.completedFuture(new BungeeSenderInfo(player));
|
||||
}
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
final ProfileLookup profile = doUUIDLookup(name);
|
||||
return new ProxyOfflineSenderInfo(profile.uuid(), profile.name());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<@Nullable SenderInfo> getOfflinePlayer(final UUID uuid) {
|
||||
final ProxiedPlayer player = getProxy().getPlayer(uuid);
|
||||
return CompletableFuture.completedFuture(player != null ? new BungeeSenderInfo(player) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Server getServer(final String server) {
|
||||
final ServerInfo serverInfo = getProxy().getServerInfo(server);
|
||||
return serverInfo != null ? new BungeeServer(serverInfo) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getServers() {
|
||||
return getProxy().getServers().keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getServerNameOf(final SenderInfo sender) {
|
||||
final ProxiedPlayer player = getProxy().getPlayer(sender.uuid());
|
||||
if (player == null || player.getServer() == null) return null;
|
||||
return player.getServer().getInfo().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void async(final Runnable runnable) {
|
||||
getProxy().getScheduler().runAsync(plugin, runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void executeConsoleCommand(final String command) {
|
||||
getProxy().getPluginManager().dispatchCommand(getProxy().getConsole(), command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcast(final Component component) {
|
||||
audiences.all().sendMessage(component);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDataFolder() {
|
||||
return plugin.getDataFolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getPluginFile() {
|
||||
return plugin.getPluginFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getOnlinePlayers() {
|
||||
return getProxy().getOnlineCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getMaxPlayers() {
|
||||
return getProxy().getConfig().getPlayerLimit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResource(final String name) {
|
||||
return plugin.getResourceAsStream(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getLogger() {
|
||||
return plugin.getLogger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerVersion() {
|
||||
return getProxy().getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PluginDump> getPlugins() {
|
||||
return getProxy().getPluginManager().getPlugins().stream().map(plugin ->
|
||||
new PluginDump(plugin.getDescription().getName(), plugin.getDescription().getVersion(), Arrays.asList(plugin.getDescription().getAuthor()))).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadIcon(final File file) throws IOException {
|
||||
favicon = Favicon.create(ImageIO.read(file));
|
||||
}
|
||||
|
||||
public boolean hasPermission(final CommandSender sender, final String permission) {
|
||||
return sender.hasPermission("maintenance." + permission) || sender.hasPermission("maintenance.admin");
|
||||
}
|
||||
|
||||
public ProxyServer getProxy() {
|
||||
return plugin.getProxy();
|
||||
}
|
||||
|
||||
public Favicon getFavicon() {
|
||||
return favicon;
|
||||
}
|
||||
|
||||
public BungeeAudiences audiences() {
|
||||
return audiences;
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.bungee.command;
|
||||
|
||||
import eu.kennytv.maintenance.bungee.MaintenanceBungeePlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.SettingsProxy;
|
||||
import eu.kennytv.maintenance.core.proxy.command.MaintenanceProxyCommand;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
public final class MaintenanceBungeeCommand extends MaintenanceProxyCommand {
|
||||
private final MaintenanceBungeePlugin plugin;
|
||||
|
||||
public MaintenanceBungeeCommand(final MaintenanceBungeePlugin plugin, final SettingsProxy settings) {
|
||||
super(plugin, settings);
|
||||
this.plugin = plugin;
|
||||
registerCommands();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getServersCompletion(final String s) {
|
||||
final List<String> list = new ArrayList<>();
|
||||
for (final Map.Entry<String, ServerInfo> entry : plugin.getProxy().getServers().entrySet()) {
|
||||
final String serverName = entry.getValue().getName();
|
||||
if (entry.getKey().toLowerCase(Locale.ROOT).startsWith(s) && !plugin.getSettingsProxy().getMaintenanceServers().contains(serverName)) {
|
||||
list.add(serverName);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getPlayersCompletion() {
|
||||
final List<String> list = new ArrayList<>();
|
||||
for (final ProxiedPlayer p : plugin.getProxy().getPlayers()) {
|
||||
list.add(p.getName());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.bungee.command;
|
||||
|
||||
import eu.kennytv.maintenance.bungee.util.BungeeSenderInfo;
|
||||
import eu.kennytv.maintenance.core.command.MaintenanceCommand;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.plugin.Command;
|
||||
import net.md_5.bungee.api.plugin.TabExecutor;
|
||||
|
||||
public final class MaintenanceBungeeCommandBase extends Command implements TabExecutor {
|
||||
private final MaintenanceCommand command;
|
||||
|
||||
public MaintenanceBungeeCommandBase(final MaintenanceCommand command) {
|
||||
super("maintenance", "maintenance.command", "mt");
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(final CommandSender sender) {
|
||||
return sender.hasPermission("maintenance.admin") || sender.hasPermission("maintenance.command");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final CommandSender sender, final String[] args) {
|
||||
command.execute(new BungeeSenderInfo(sender), args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> onTabComplete(final CommandSender sender, final String[] args) {
|
||||
return command.getSuggestions(new BungeeSenderInfo(sender), args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.bungee.listener;
|
||||
|
||||
import eu.kennytv.maintenance.bungee.MaintenanceBungeePlugin;
|
||||
import eu.kennytv.maintenance.bungee.util.ComponentUtil;
|
||||
import eu.kennytv.maintenance.core.proxy.SettingsProxy;
|
||||
import net.md_5.bungee.api.ServerPing;
|
||||
import net.md_5.bungee.api.event.ProxyPingEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
|
||||
public final class ProxyPingListener implements Listener {
|
||||
private final MaintenanceBungeePlugin plugin;
|
||||
private final SettingsProxy settings;
|
||||
|
||||
public ProxyPingListener(final MaintenanceBungeePlugin plugin, final SettingsProxy settings) {
|
||||
this.plugin = plugin;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
@EventHandler(priority = 80)
|
||||
public void proxyPing(final ProxyPingEvent event) {
|
||||
if (!settings.isMaintenance()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ServerPing ping = event.getResponse();
|
||||
if (settings.hasCustomPlayerCountMessage()) {
|
||||
ping.setVersion(new ServerPing.Protocol(settings.getLegacyParsedPlayerCountMessage(), 1));
|
||||
}
|
||||
|
||||
if (settings.isEnablePingMessages()) {
|
||||
ping.setDescriptionComponent(ComponentUtil.toBadComponents(settings.getRandomPingMessage()));
|
||||
}
|
||||
|
||||
if (settings.hasCustomPlayerCountHoverMessage()) {
|
||||
ServerPing.Players players = ping.getPlayers();
|
||||
if (players == null) {
|
||||
players = new ServerPing.Players(0, 0, null);
|
||||
ping.setPlayers(players);
|
||||
}
|
||||
|
||||
final String[] lines = settings.getLegacyParsedPlayerCountHoverLines();
|
||||
final ServerPing.PlayerInfo[] samplePlayers = new ServerPing.PlayerInfo[lines.length];
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
samplePlayers[i] = new ServerPing.PlayerInfo(lines[i], "");
|
||||
}
|
||||
players.setSample(samplePlayers);
|
||||
}
|
||||
|
||||
if (settings.hasCustomIcon() && plugin.getFavicon() != null) {
|
||||
ping.setFavicon(plugin.getFavicon());
|
||||
}
|
||||
}
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.bungee.listener;
|
||||
|
||||
import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.bungee.MaintenanceBungeePlugin;
|
||||
import eu.kennytv.maintenance.bungee.util.BungeeSenderInfo;
|
||||
import eu.kennytv.maintenance.bungee.util.BungeeServer;
|
||||
import eu.kennytv.maintenance.core.proxy.SettingsProxy;
|
||||
import eu.kennytv.maintenance.core.proxy.listener.ProxyJoinListenerBase;
|
||||
import eu.kennytv.maintenance.core.proxy.util.ServerConnectResult;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.ServerConnectEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
import net.md_5.bungee.event.EventPriority;
|
||||
|
||||
public final class ServerConnectListener extends ProxyJoinListenerBase implements Listener {
|
||||
private final MaintenanceBungeePlugin plugin;
|
||||
|
||||
public ServerConnectListener(final MaintenanceBungeePlugin plugin, final SettingsProxy settings) {
|
||||
super(plugin, settings);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void initialServerConnect(final ServerConnectEvent event) {
|
||||
// Global maintenance check
|
||||
if (event.isCancelled() || event.getReason() != ServerConnectEvent.Reason.JOIN_PROXY) return;
|
||||
|
||||
final BungeeSenderInfo player = new BungeeSenderInfo(event.getPlayer());
|
||||
if (plugin.isDebug()) {
|
||||
plugin.getLogger().info("Join permission check for " + event.getPlayer().getName()
|
||||
+ " - Permission: " + player.hasMaintenancePermission("bypass") + ", whitelist: " + settings.isWhitelisted(player.uuid()));
|
||||
}
|
||||
|
||||
if (shouldKick(player)) {
|
||||
final Server waitingServer = shouldConnectToWaitingServer(player);
|
||||
if (waitingServer != null) {
|
||||
event.setTarget(((BungeeServer) waitingServer).server());
|
||||
player.send(settings.getMessage("sentToWaitingServer"));
|
||||
|
||||
if (plugin.isDebug()) {
|
||||
plugin.getLogger().info("Join to waiting server for " + event.getPlayer().getName());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
|
||||
player.disconnect(settings.getKickMessage());
|
||||
if (settings.isJoinNotifications()) {
|
||||
broadcastJoinNotification(player.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void serverConnect(final ServerConnectEvent event) {
|
||||
// Server specific maintenance check
|
||||
if (event.isCancelled()) return;
|
||||
|
||||
final ProxiedPlayer proxiedPlayer = event.getPlayer();
|
||||
final boolean normalServerConnect = event.getReason() != ServerConnectEvent.Reason.JOIN_PROXY && event.getReason() != ServerConnectEvent.Reason.KICK_REDIRECT
|
||||
&& event.getReason() != ServerConnectEvent.Reason.LOBBY_FALLBACK && event.getReason() != ServerConnectEvent.Reason.SERVER_DOWN_REDIRECT;
|
||||
final BungeeSenderInfo player = new BungeeSenderInfo(proxiedPlayer);
|
||||
final ServerConnectResult connectResult = serverConnect(player, new BungeeServer(event.getTarget()), normalServerConnect);
|
||||
if (plugin.isDebug()) {
|
||||
plugin.getLogger().info("Connectresult for " + player.name() + " to " + event.getTarget().getName() + ": " + connectResult);
|
||||
}
|
||||
|
||||
if (connectResult.isCancelled()) {
|
||||
event.setCancelled(true);
|
||||
|
||||
// Player has no server to connect to
|
||||
if (proxiedPlayer.getServer() == null) {
|
||||
player.disconnect(settings.getKickMessage());
|
||||
}
|
||||
} else if (connectResult.getTarget() != null) {
|
||||
event.setTarget(((BungeeServer) connectResult.getTarget()).server());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void broadcastJoinNotification(final String name) {
|
||||
sendJoinMessage(ProxyServer.getInstance().getPlayers(), name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void broadcastJoinNotification(final String name, final Server server) {
|
||||
sendJoinMessage(((BungeeServer) server).server().getPlayers(), name);
|
||||
}
|
||||
|
||||
private void sendJoinMessage(final Iterable<ProxiedPlayer> players, final String name) {
|
||||
final Component component = settings.getMessage("joinNotification", "%PLAYER%", name);
|
||||
for (final ProxiedPlayer player : players) {
|
||||
if (plugin.hasPermission(player, "joinnotification")) {
|
||||
plugin.audiences().player(player).sendMessage(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.bungee.util;
|
||||
|
||||
import eu.kennytv.maintenance.api.MaintenanceProvider;
|
||||
import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.bungee.MaintenanceBungeePlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.util.ProxySenderInfo;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.connection.Connection;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public final class BungeeSenderInfo implements ProxySenderInfo {
|
||||
private final CommandSender sender;
|
||||
|
||||
public BungeeSenderInfo(final CommandSender sender) {
|
||||
this.sender = sender;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID uuid() {
|
||||
return sender instanceof ProxiedPlayer proxiedPlayer ? proxiedPlayer.getUniqueId() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return sender.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(final String permission) {
|
||||
return sender.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(final Component component) {
|
||||
((MaintenanceBungeePlugin) MaintenanceProvider.get()).audiences().sender(sender).sendMessage(component);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayer() {
|
||||
return sender instanceof ProxiedPlayer;
|
||||
}
|
||||
|
||||
public void sendMessage(final TextComponent textComponent) {
|
||||
sender.sendMessage(textComponent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAccess(final Server server) {
|
||||
return ((BungeeServer) server).server().canAccess(sender);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(final Component component) {
|
||||
if (sender instanceof Connection connection) {
|
||||
connection.disconnect(ComponentUtil.toBadComponent(component));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.bungee.util;
|
||||
|
||||
import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
public record BungeeServer(ServerInfo server) implements Server {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return server.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPlayers() {
|
||||
return !server.getPlayers().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcast(final Component component) {
|
||||
final BaseComponent[] components = ComponentUtil.toBadComponent(component);
|
||||
for (final ProxiedPlayer player : server.getPlayers()) {
|
||||
player.sendMessage(components);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegisteredServer() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -15,20 +15,20 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.paper.util;
|
||||
package eu.kennytv.maintenance.bungee.util;
|
||||
|
||||
import eu.kennytv.maintenance.core.util.Task;
|
||||
import org.bukkit.Bukkit;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
|
||||
public final class BukkitTask implements Task {
|
||||
public final class BungeeTask implements Task {
|
||||
private final int id;
|
||||
|
||||
public BukkitTask(final int id) {
|
||||
public BungeeTask(final int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
Bukkit.getScheduler().cancelTask(id);
|
||||
ProxyServer.getInstance().getScheduler().cancel(id);
|
||||
}
|
||||
}
|
||||
+10
-5
@@ -15,16 +15,21 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.velocity.util;
|
||||
package eu.kennytv.maintenance.bungee.util;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
|
||||
public final class ComponentUtil {
|
||||
|
||||
public static Component toVelocity(final eu.kennytv.maintenance.lib.kyori.adventure.text.Component component) {
|
||||
final JsonElement json = eu.kennytv.maintenance.lib.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson().serializeToTree(component);
|
||||
return GsonComponentSerializer.gson().deserializeFromTree(json);
|
||||
public static BaseComponent[] toBadComponent(final Component component) {
|
||||
return ComponentSerializer.parse(GsonComponentSerializer.gson().serialize(component));
|
||||
}
|
||||
|
||||
public static BaseComponent toBadComponents(final Component component) {
|
||||
return new TextComponent(ComponentSerializer.parse(GsonComponentSerializer.gson().serialize(component)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
name: Maintenance
|
||||
version: ${version}
|
||||
main: eu.kennytv.maintenance.bungee.MaintenanceBungeeBase
|
||||
author: kennytv
|
||||
description: ${description}
|
||||
softDepends: [ServerListPlus, LuckPerms]
|
||||
website: https://hangar.papermc.io/kennytv/Maintenance
|
||||
@@ -1,7 +1,13 @@
|
||||
dependencies {
|
||||
api(projects.maintenanceApiProxy)
|
||||
api(projects.maintenanceCore)
|
||||
implementation(libs.hikariCP)
|
||||
|
||||
// Try to reasonably minimize it...
|
||||
implementation(libs.jedis) {
|
||||
exclude("com.google.code.gson", "gson")
|
||||
exclude("org.slf4j", "slf4j-api")
|
||||
}
|
||||
|
||||
compileOnly(libs.luckperms)
|
||||
compileOnly(libs.guava)
|
||||
compileOnly(libs.gson)
|
||||
|
||||
+86
-45
@@ -28,14 +28,15 @@ import eu.kennytv.maintenance.core.proxy.runnable.SingleMaintenanceRunnable;
|
||||
import eu.kennytv.maintenance.core.proxy.runnable.SingleMaintenanceScheduleRunnable;
|
||||
import eu.kennytv.maintenance.core.proxy.util.ProfileLookup;
|
||||
import eu.kennytv.maintenance.core.runnable.MaintenanceRunnableBase;
|
||||
import eu.kennytv.maintenance.core.util.DiscordWebhook;
|
||||
import eu.kennytv.maintenance.core.util.RateLimitedException;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.core.util.ServerType;
|
||||
import eu.kennytv.maintenance.core.util.Task;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
@@ -46,6 +47,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Pattern;
|
||||
import org.jetbrains.annotations.Blocking;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -54,7 +57,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class MaintenanceProxyPlugin extends MaintenancePlugin implements MaintenanceProxy {
|
||||
private final Map<String, Task> serverTasks = new HashMap<>();
|
||||
private static final Pattern USERNAME_PATTERN = Pattern.compile("^[A-Za-z0-9_.]{1,16}$");
|
||||
private final Map<String, MaintenanceRunnableBase> serverTasks = new HashMap<>();
|
||||
protected SettingsProxy settingsProxy;
|
||||
|
||||
protected MaintenanceProxyPlugin(final String version, final ServerType serverType) {
|
||||
@@ -64,49 +68,54 @@ public abstract class MaintenanceProxyPlugin extends MaintenancePlugin implement
|
||||
@Override
|
||||
public void disable() {
|
||||
super.disable();
|
||||
if (settingsProxy.getMySQL() != null) {
|
||||
settingsProxy.getMySQL().close();
|
||||
if (settingsProxy.redisHandler() != null) {
|
||||
settingsProxy.redisHandler().close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaintenance(final boolean maintenance) {
|
||||
if (settingsProxy.hasMySQL()) {
|
||||
settingsProxy.setMaintenanceToSQL(maintenance);
|
||||
}
|
||||
super.setMaintenance(maintenance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMaintenance(final Server server) {
|
||||
return settingsProxy.isMaintenance(server.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setMaintenanceToServer(final Server server, final boolean maintenance) {
|
||||
public boolean setMaintenanceToServer(final Server server, final boolean maintenance, @Nullable final String mode) {
|
||||
if (maintenance) {
|
||||
if (!settingsProxy.addMaintenanceServer(server.getName())) {
|
||||
return false;
|
||||
if (settingsProxy.addMaintenanceServer(server.getName(), mode)) {
|
||||
serverActions(server, true);
|
||||
return true;
|
||||
}
|
||||
} else if (!settingsProxy.removeMaintenanceServer(server.getName())) {
|
||||
return mode != null && settingsProxy.setMaintenanceServerMode(server.getName(), mode);
|
||||
}
|
||||
|
||||
if (!settingsProxy.removeMaintenanceServer(server.getName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
serverActions(server, maintenance);
|
||||
|
||||
for (final String command : (maintenance ? settingsProxy.getCommandsOnMaintenanceEnable(server) : settingsProxy.getCommandsOnMaintenanceDisable(server))) {
|
||||
try {
|
||||
executeConsoleCommand(command.replace("%SERVER%", server.getName()));
|
||||
} catch (final Exception e) {
|
||||
getLogger().severe("Error while executing extra maintenance " + (maintenance ? "enable" : "disable") + " command: " + command);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
serverActions(server, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setMaintenanceToServer(final Server server, final boolean maintenance) {
|
||||
return setMaintenanceToServer(server, maintenance, null);
|
||||
}
|
||||
|
||||
public boolean setMaintenanceModeToServer(final Server server, @Nullable final String mode) {
|
||||
if (!settingsProxy.isMaintenance(server.getName())) {
|
||||
return false;
|
||||
}
|
||||
return settingsProxy.setMaintenanceServerMode(server.getName(), mode);
|
||||
}
|
||||
|
||||
public @Nullable String activeMode(final Server server) {
|
||||
return settingsProxy.activeMode(server.getName());
|
||||
}
|
||||
|
||||
public void serverActions(final Server server, final boolean maintenance) {
|
||||
if (server == null) return;
|
||||
if (server == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip to the even fire for dummy servers
|
||||
if (server.isRegisteredServer()) {
|
||||
@@ -126,6 +135,19 @@ public abstract class MaintenanceProxyPlugin extends MaintenancePlugin implement
|
||||
}
|
||||
|
||||
eventManager.callEvent(new ServerMaintenanceChangedEvent(server, maintenance));
|
||||
if (maintenance) {
|
||||
sendWebhookMessage("webhookSingleMaintenanceActivated", DiscordWebhook.EventType.MAINTENANCE_ENABLED, "%SERVER%", server.getName());
|
||||
} else {
|
||||
sendWebhookMessage("webhookSingleMaintenanceDeactivated", DiscordWebhook.EventType.MAINTENANCE_DISABLED, "%SERVER%", server.getName());
|
||||
}
|
||||
|
||||
for (final String command : (maintenance ? settingsProxy.getCommandsOnMaintenanceEnable(server) : settingsProxy.getCommandsOnMaintenanceDisable(server))) {
|
||||
try {
|
||||
executeConsoleCommand(command.replace("%SERVER%", server.getName()));
|
||||
} catch (final Exception e) {
|
||||
getLogger().log(Level.SEVERE, "Error while executing extra maintenance " + (maintenance ? "enable" : "disable") + " command: " + command, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -133,28 +155,45 @@ public abstract class MaintenanceProxyPlugin extends MaintenancePlugin implement
|
||||
return serverTasks.containsKey(server.getName());
|
||||
}
|
||||
|
||||
public boolean hasServerTasks() {
|
||||
return !serverTasks.isEmpty();
|
||||
}
|
||||
|
||||
public Map<String, MaintenanceRunnableBase> getServerTasks() {
|
||||
return serverTasks;
|
||||
}
|
||||
|
||||
public MaintenanceRunnableBase getServerTask(final String server) {
|
||||
return serverTasks.get(server);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getMaintenanceServers() {
|
||||
return Collections.unmodifiableSet(settingsProxy.getMaintenanceServers());
|
||||
}
|
||||
|
||||
public void cancelSingleTask(final Server server) {
|
||||
final Task task = serverTasks.remove(server.getName());
|
||||
final MaintenanceRunnableBase task = serverTasks.remove(server.getName());
|
||||
if (task != null) {
|
||||
task.cancel();
|
||||
task.getTask().cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public MaintenanceRunnableBase startSingleMaintenanceRunnable(final Server server, final Duration duration, final boolean enable) {
|
||||
final MaintenanceRunnableBase runnable = new SingleMaintenanceRunnable(this, settingsProxy, (int) duration.getSeconds(), enable, server);
|
||||
serverTasks.put(server.getName(), runnable.getTask());
|
||||
public MaintenanceRunnableBase startSingleMaintenanceRunnable(final Server server, final Duration duration, final boolean enable, @Nullable final String mode) {
|
||||
final MaintenanceRunnableBase runnable = new SingleMaintenanceRunnable(this, settingsProxy, (int) duration.getSeconds(), enable, server, mode);
|
||||
serverTasks.put(server.getName(), runnable);
|
||||
return runnable;
|
||||
}
|
||||
|
||||
public MaintenanceRunnableBase scheduleSingleMaintenanceRunnable(final Server server, final Duration enableIn, final Duration maintenanceDuration) {
|
||||
public MaintenanceRunnableBase startSingleMaintenanceRunnable(final Server server, final Duration duration, final boolean enable) {
|
||||
return startSingleMaintenanceRunnable(server, duration, enable, null);
|
||||
}
|
||||
|
||||
public MaintenanceRunnableBase scheduleSingleMaintenanceRunnable(final Server server, final Duration enableIn,
|
||||
final Duration maintenanceDuration, @Nullable final String mode) {
|
||||
final MaintenanceRunnableBase runnable = new SingleMaintenanceScheduleRunnable(this, settingsProxy,
|
||||
(int) enableIn.getSeconds(), (int) maintenanceDuration.getSeconds(), server);
|
||||
serverTasks.put(server.getName(), runnable.getTask());
|
||||
(int) enableIn.getSeconds(), (int) maintenanceDuration.getSeconds(), server, mode);
|
||||
serverTasks.put(server.getName(), runnable);
|
||||
return runnable;
|
||||
}
|
||||
|
||||
@@ -192,15 +231,17 @@ public abstract class MaintenanceProxyPlugin extends MaintenancePlugin implement
|
||||
@Blocking
|
||||
@Nullable
|
||||
protected ProfileLookup doUUIDLookup(final String name) throws IOException {
|
||||
ProfileLookup profileLookup;
|
||||
try {
|
||||
profileLookup = doUUIDLookupMojangAPI(name);
|
||||
} catch (RateLimitedException e) {
|
||||
// Use fallback API if rate limit is reached
|
||||
profileLookup = doUUIDLookupAshconAPI(name);
|
||||
ProfileLookup profileLookup = null;
|
||||
if (USERNAME_PATTERN.matcher(name).matches()) {
|
||||
try {
|
||||
profileLookup = doUUIDLookupMojangAPI(name);
|
||||
} catch (RateLimitedException e) {
|
||||
// Use fallback API if rate limit is reached
|
||||
profileLookup = doUUIDLookupAshconAPI(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (settingsProxy.isFallbackToOfflineUUID() && profileLookup == null) {
|
||||
if (profileLookup == null && settingsProxy.isFallbackToOfflineUUID()) {
|
||||
// Use offline uuid
|
||||
return new ProfileLookup(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(StandardCharsets.UTF_8)), name);
|
||||
}
|
||||
@@ -213,7 +254,7 @@ public abstract class MaintenanceProxyPlugin extends MaintenancePlugin implement
|
||||
*/
|
||||
@Nullable
|
||||
private ProfileLookup doUUIDLookupMojangAPI(final String name) throws IOException {
|
||||
final URL url = new URL("https://api.mojang.com/users/profiles/minecraft/" + name);
|
||||
final URL url = URI.create("https://api.mojang.com/users/profiles/minecraft/" + name).toURL();
|
||||
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
|
||||
@@ -241,7 +282,7 @@ public abstract class MaintenanceProxyPlugin extends MaintenancePlugin implement
|
||||
*/
|
||||
@Nullable
|
||||
private ProfileLookup doUUIDLookupAshconAPI(final String name) throws IOException {
|
||||
final URL url = new URL("https://api.ashcon.app/mojang/v2/user/" + name);
|
||||
final URL url = URI.create("https://api.ashcon.app/mojang/v2/user/" + name).toURL();
|
||||
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
|
||||
if (connection.getResponseCode() == 403) {
|
||||
@@ -279,4 +320,4 @@ public abstract class MaintenanceProxyPlugin extends MaintenancePlugin implement
|
||||
protected abstract void kickPlayersTo(Server server);
|
||||
|
||||
protected abstract void kickPlayersFromProxy();
|
||||
}
|
||||
}
|
||||
@@ -17,88 +17,55 @@
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.proxy;
|
||||
|
||||
import eu.kennytv.maintenance.api.proxy.MaintenanceProxy;
|
||||
import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.core.Settings;
|
||||
import eu.kennytv.maintenance.core.config.ConfigSection;
|
||||
import eu.kennytv.maintenance.core.proxy.mysql.MySQL;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import eu.kennytv.maintenance.core.proxy.redis.RedisHandler;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class SettingsProxy extends Settings {
|
||||
private final MaintenanceProxyPlugin proxyPlugin;
|
||||
private Set<String> maintenanceServers;
|
||||
|
||||
private Map<String, String> maintenanceServers;
|
||||
private List<String> fallbackServers;
|
||||
private String waitingServer;
|
||||
private boolean fallbackToOfflineUUID;
|
||||
|
||||
private Map<String, List<String>> commandsOnMaintenanceEnable;
|
||||
private Map<String, List<String>> commandsOnMaintenanceDisable;
|
||||
private String mySQLTable;
|
||||
private String serverTable;
|
||||
private String maintenanceQuery;
|
||||
private String serverQuery;
|
||||
private MySQL mySQL;
|
||||
|
||||
private long millisecondsToCheck;
|
||||
private long lastMySQLCheck;
|
||||
private long lastServerCheck;
|
||||
private RedisHandler redisHandler;
|
||||
|
||||
public SettingsProxy(final MaintenanceProxyPlugin plugin) {
|
||||
super(plugin);
|
||||
this.proxyPlugin = plugin;
|
||||
}
|
||||
|
||||
private void setupMySQL() {
|
||||
plugin.getLogger().info("Trying to open database connection... (also, you can simply ignore the SLF4J soft-warning if it shows up)");
|
||||
final ConfigSection section = config.getSection("mysql");
|
||||
if (section == null) {
|
||||
plugin.getLogger().warning("Section missing: mysql");
|
||||
return;
|
||||
}
|
||||
|
||||
mySQL = new MySQL(plugin.getLogger(),
|
||||
section.getString("host"),
|
||||
section.getInt("port"),
|
||||
section.getString("username"),
|
||||
section.getString("password"),
|
||||
section.getString("database"),
|
||||
section.getBoolean("use-ssl", true));
|
||||
|
||||
// Varchar as the value regarding the possibility of saving stuff like the motd as well in future updates
|
||||
mySQLTable = section.getString("table", "maintenance_settings");
|
||||
serverTable = section.getString("servertable", "maintenance_servers");
|
||||
mySQL.executeUpdate("CREATE TABLE IF NOT EXISTS " + mySQLTable + " (setting VARCHAR(16) PRIMARY KEY, value VARCHAR(255))");
|
||||
mySQL.executeUpdate("CREATE TABLE IF NOT EXISTS " + serverTable + " (server VARCHAR(64) PRIMARY KEY)");
|
||||
maintenanceQuery = "SELECT * FROM " + mySQLTable + " WHERE setting = ?";
|
||||
serverQuery = "SELECT * FROM " + serverTable;
|
||||
plugin.getLogger().info("Done!");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadExtraSettings() {
|
||||
// Open database connection if enabled and not already done
|
||||
if (!hasMySQL() && config.getBoolean("mysql.use-mysql")) {
|
||||
try {
|
||||
setupMySQL();
|
||||
} catch (final Exception e) {
|
||||
mySQL = null;
|
||||
plugin.getLogger().warning("Error while trying do open database connection!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
// Open Redis connection if enabled and not already done
|
||||
final ConfigSection redisSection = config.getSection("redis");
|
||||
if (!hasRedis() && redisSection.getBoolean("enabled")) {
|
||||
connectToRedis(redisSection.getString("uri"));
|
||||
}
|
||||
if (hasRedis()) {
|
||||
maintenance = redisHandler.loadMaintenanceStatus();
|
||||
activeMode = normalizeMode(redisHandler.loadGlobalMode());
|
||||
maintenanceServers = redisHandler.loadMaintenanceServers();
|
||||
}
|
||||
|
||||
final Object fallback = config.getObject("fallback");
|
||||
fallbackServers = fallback instanceof String ? Collections.singletonList((String) fallback) : config.getStringList("fallback", Collections.emptyList());
|
||||
fallbackServers = fallback instanceof String s ? Collections.singletonList(s) : config.getStringList("fallback", Collections.emptyList());
|
||||
|
||||
waitingServer = config.getString("waiting-server", "");
|
||||
if (waitingServer.isEmpty() || waitingServer.equalsIgnoreCase("none")) {
|
||||
@@ -118,55 +85,36 @@ public final class SettingsProxy extends Settings {
|
||||
commandsOnMaintenanceDisable.put(key.toLowerCase(Locale.ROOT), disableCommandsSection.getStringList(key));
|
||||
}
|
||||
|
||||
if (hasMySQL()) {
|
||||
maintenance = loadMaintenance();
|
||||
maintenanceServers = loadMaintenanceServersFromSQL();
|
||||
|
||||
final long configValue = config.getInt("mysql.update-interval");
|
||||
// Even if set to 0, only check every 500 millis
|
||||
millisecondsToCheck = configValue > 0 ? configValue * 1000 : 500;
|
||||
lastMySQLCheck = System.currentTimeMillis();
|
||||
lastServerCheck = System.currentTimeMillis();
|
||||
} else {
|
||||
final List<String> list = config.getStringList("proxied-maintenance-servers");
|
||||
maintenanceServers = list == null ? new HashSet<>() : new HashSet<>(list);
|
||||
if (!hasRedis()) {
|
||||
final ConfigSection maintenanceServersSection = config.getSection("proxied-maintenance-servers");
|
||||
maintenanceServers = new LinkedHashMap<>();
|
||||
for (final Map.Entry<String, ?> entry : maintenanceServersSection.getValues().entrySet()) {
|
||||
if (entry.getKey() != null) {
|
||||
maintenanceServers.put(entry.getKey(), normalizeServerMode(entry.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMaintenance() {
|
||||
if (hasMySQL() && System.currentTimeMillis() - lastMySQLCheck > millisecondsToCheck) {
|
||||
final boolean databaseValue = loadMaintenance();
|
||||
if (databaseValue != maintenance) {
|
||||
maintenance = databaseValue;
|
||||
plugin.serverActions(maintenance);
|
||||
}
|
||||
lastMySQLCheck = System.currentTimeMillis();
|
||||
private void connectToRedis(final String redisUri) {
|
||||
redisHandler = new RedisHandler((MaintenanceProxyPlugin) plugin, this);
|
||||
try {
|
||||
redisHandler.setup(redisUri);
|
||||
} catch (final Exception e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Error while trying to open Redis connection!", e);
|
||||
e.printStackTrace();
|
||||
redisHandler.close();
|
||||
redisHandler = null;
|
||||
}
|
||||
return maintenance;
|
||||
}
|
||||
|
||||
public boolean isMaintenance(final String serverName) {
|
||||
if (hasMySQL() && System.currentTimeMillis() - lastServerCheck > millisecondsToCheck) {
|
||||
final Set<String> databaseValue = loadMaintenanceServersFromSQL();
|
||||
if (!maintenanceServers.equals(databaseValue)) {
|
||||
// Enable maintenance on yet unlisted servers
|
||||
for (final String s : databaseValue) {
|
||||
if (!maintenanceServers.contains(s)) {
|
||||
proxyPlugin.serverActions(proxyPlugin.getServer(s), true);
|
||||
}
|
||||
}
|
||||
// Disable maintenance on now unlisted servers
|
||||
for (final String s : maintenanceServers) {
|
||||
if (!databaseValue.contains(s)) {
|
||||
proxyPlugin.serverActions(proxyPlugin.getServer(s), false);
|
||||
}
|
||||
}
|
||||
maintenanceServers = databaseValue;
|
||||
}
|
||||
lastServerCheck = System.currentTimeMillis();
|
||||
}
|
||||
return maintenanceServers.contains(serverName);
|
||||
return maintenanceServers.containsKey(serverName);
|
||||
}
|
||||
|
||||
public @Nullable String activeMode(final String serverName) {
|
||||
final String mode = maintenanceServers.get(serverName);
|
||||
return mode == null || mode.equals("default") ? null : mode;
|
||||
}
|
||||
|
||||
public Component getServerKickMessage(final String server) {
|
||||
@@ -174,101 +122,154 @@ public final class SettingsProxy extends Settings {
|
||||
if (message == null) {
|
||||
message = getLanguageString("singleMaintenanceKick", "%SERVER%", server);
|
||||
}
|
||||
return parse(plugin.replacePingVariables(message));
|
||||
return parse(plugin.replacePingVariables(message, activeMode(server)));
|
||||
}
|
||||
|
||||
// Full = being kicked from the proxy, not just a proxied server
|
||||
public Component getFullServerKickMessage(final String server) {
|
||||
String message = getLanguageStringOrNull("singleMaintenanceKicksComplete." + server, "%SERVER%", server);
|
||||
if (message == null) {
|
||||
message = getLanguageString("singleMaintenanceKickComplete", "%SERVER%", server);
|
||||
}
|
||||
return parse(plugin.replacePingVariables(message));
|
||||
return parse(plugin.replacePingVariables(message, activeMode(server)));
|
||||
}
|
||||
|
||||
public boolean hasMySQL() {
|
||||
return mySQL != null;
|
||||
public boolean hasRedis() {
|
||||
return redisHandler != null;
|
||||
}
|
||||
|
||||
void setMaintenanceToSQL(final boolean maintenance) {
|
||||
plugin.async(() -> {
|
||||
final String s = String.valueOf(maintenance);
|
||||
mySQL.executeUpdate("INSERT INTO " + mySQLTable + " (setting, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value = ?", "maintenance", s, s);
|
||||
lastMySQLCheck = System.currentTimeMillis();
|
||||
});
|
||||
}
|
||||
|
||||
boolean addMaintenanceServer(final String server) {
|
||||
if (hasMySQL()) {
|
||||
maintenanceServers = loadMaintenanceServersFromSQL();
|
||||
if (!maintenanceServers.add(server)) return false;
|
||||
plugin.async(() -> mySQL.executeUpdate("INSERT INTO " + serverTable + " (server) VALUES (?)", server));
|
||||
lastServerCheck = System.currentTimeMillis();
|
||||
@Override
|
||||
protected void loadWhitelistedPlayers() {
|
||||
if (hasRedis()) {
|
||||
redisHandler.loadPlayers(getWhitelistedPlayers());
|
||||
} else {
|
||||
super.loadWhitelistedPlayers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaintenance(final boolean maintenance, @Nullable final String mode) {
|
||||
super.setMaintenance(maintenance, mode);
|
||||
if (hasRedis()) {
|
||||
redisHandler.set(maintenance, mode != null ? mode : "default");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActiveMode(@Nullable final String activeMode) {
|
||||
super.setActiveMode(activeMode);
|
||||
if (hasRedis() && isMaintenance()) {
|
||||
redisHandler.set(true, activeMode != null ? activeMode : "default");
|
||||
}
|
||||
}
|
||||
|
||||
public void setActiveModeDirect(@Nullable final String activeMode) {
|
||||
super.setActiveMode(activeMode);
|
||||
}
|
||||
|
||||
public void setMaintenanceDirect(final boolean maintenance, @Nullable final String mode) {
|
||||
super.setMaintenance(maintenance, mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addWhitelistedPlayer(final UUID uuid, final String name) {
|
||||
if (super.addWhitelistedPlayer(uuid, name)) {
|
||||
if (hasRedis()) {
|
||||
redisHandler.addPlayer(uuid, name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean addWhitelistedPlayerDirect(final UUID uuid, final String name) {
|
||||
return super.addWhitelistedPlayer(uuid, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeWhitelistedPlayer(final UUID uuid) {
|
||||
if (super.removeWhitelistedPlayer(uuid)) {
|
||||
if (hasRedis()) {
|
||||
redisHandler.removePlayer(uuid);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean removeWhitelistedPlayerDirect(final UUID uuid) {
|
||||
return super.removeWhitelistedPlayer(uuid);
|
||||
}
|
||||
|
||||
boolean addMaintenanceServer(final String server, @Nullable final String mode) {
|
||||
final String normalizedMode = normalizeServerMode(mode);
|
||||
if (hasRedis()) {
|
||||
if (redisHandler.addServer(server, normalizedMode)) {
|
||||
maintenanceServers.put(server, normalizedMode);
|
||||
return true;
|
||||
}
|
||||
} else if (maintenanceServers.putIfAbsent(server, normalizedMode) == null) {
|
||||
saveServersToConfig();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean setMaintenanceServerMode(final String server, @Nullable final String mode) {
|
||||
final String normalizedMode = normalizeServerMode(mode);
|
||||
final String oldMode = maintenanceServers.get(server);
|
||||
if (oldMode == null || oldMode.equals(normalizedMode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
maintenanceServers.put(server, normalizedMode);
|
||||
if (hasRedis()) {
|
||||
redisHandler.setServerMode(server, normalizedMode);
|
||||
} else {
|
||||
if (!maintenanceServers.add(server)) return false;
|
||||
saveServersToConfig();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean removeMaintenanceServer(final String server) {
|
||||
if (hasMySQL()) {
|
||||
maintenanceServers = loadMaintenanceServersFromSQL();
|
||||
if (!maintenanceServers.remove(server)) return false;
|
||||
|
||||
plugin.async(() -> mySQL.executeUpdate("DELETE FROM " + serverTable + " WHERE server = ?", server));
|
||||
lastServerCheck = System.currentTimeMillis();
|
||||
} else {
|
||||
if (!maintenanceServers.remove(server)) return false;
|
||||
|
||||
if (hasRedis()) {
|
||||
if (redisHandler.removeServer(server)) {
|
||||
maintenanceServers.remove(server);
|
||||
return true;
|
||||
}
|
||||
} else if (maintenanceServers.remove(server) != null) {
|
||||
saveServersToConfig();
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private Set<String> loadMaintenanceServersFromSQL() {
|
||||
final Set<String> maintenanceServers = new HashSet<>();
|
||||
mySQL.executeQuery(serverQuery, rs -> {
|
||||
try {
|
||||
while (rs.next()) {
|
||||
maintenanceServers.add(rs.getString("server"));
|
||||
}
|
||||
} catch (final SQLException e) {
|
||||
plugin.getLogger().warning("An error occured while trying to get the list of single servers with maintenance!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
return maintenanceServers;
|
||||
public boolean addMaintenanceServerDirect(final String server, @Nullable final String mode) {
|
||||
return maintenanceServers.put(server, normalizeServerMode(mode)) == null;
|
||||
}
|
||||
|
||||
private boolean loadMaintenance() {
|
||||
final boolean[] databaseValue = {false};
|
||||
mySQL.executeQuery(maintenanceQuery, rs -> {
|
||||
try {
|
||||
if (rs.next()) {
|
||||
databaseValue[0] = Boolean.parseBoolean(rs.getString("value"));
|
||||
}
|
||||
} catch (final SQLException e) {
|
||||
plugin.getLogger().warning("An error occured while trying to get the maintenance value from the database!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, "maintenance");
|
||||
return databaseValue[0];
|
||||
public boolean removeMaintenanceServerDirect(final String server) {
|
||||
return maintenanceServers.remove(server) != null;
|
||||
}
|
||||
|
||||
public void setMaintenanceServerModeDirect(final String server, @Nullable final String mode) {
|
||||
if (maintenanceServers.containsKey(server)) {
|
||||
maintenanceServers.put(server, normalizeServerMode(mode));
|
||||
}
|
||||
}
|
||||
|
||||
private void saveServersToConfig() {
|
||||
config.set("proxied-maintenance-servers", new ArrayList<>(maintenanceServers));
|
||||
config.set("proxied-maintenance-servers", new LinkedHashMap<>(maintenanceServers));
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
public Set<String> getMaintenanceServers() {
|
||||
return maintenanceServers;
|
||||
return maintenanceServers.keySet();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Server getFallbackServer() {
|
||||
for (final String fallbackServer : fallbackServers) {
|
||||
final Server server = proxyPlugin.getServer(fallbackServer);
|
||||
final Server server = ((MaintenanceProxy) plugin).getServer(fallbackServer);
|
||||
if (server != null && !isMaintenance(server.getName())) {
|
||||
return server;
|
||||
}
|
||||
@@ -286,7 +287,7 @@ public final class SettingsProxy extends Settings {
|
||||
}
|
||||
|
||||
public List<String> getCommandsOnMaintenanceEnable(final Server server) {
|
||||
final List<String> enableCommands = commandsOnMaintenanceEnable.getOrDefault("all", new ArrayList<>());
|
||||
final List<String> enableCommands = new ArrayList<>(commandsOnMaintenanceEnable.getOrDefault("all", Collections.emptyList()));
|
||||
final List<String> serverEnableCommands = commandsOnMaintenanceEnable.get(server.getName().toLowerCase(Locale.ROOT));
|
||||
if (serverEnableCommands != null) {
|
||||
enableCommands.addAll(serverEnableCommands);
|
||||
@@ -295,7 +296,7 @@ public final class SettingsProxy extends Settings {
|
||||
}
|
||||
|
||||
public List<String> getCommandsOnMaintenanceDisable(final Server server) {
|
||||
final List<String> disableCommands = commandsOnMaintenanceDisable.getOrDefault("all", new ArrayList<>());
|
||||
final List<String> disableCommands = new ArrayList<>(commandsOnMaintenanceDisable.getOrDefault("all", Collections.emptyList()));
|
||||
final List<String> serverDisableCommands = commandsOnMaintenanceDisable.get(server.getName().toLowerCase(Locale.ROOT));
|
||||
if (serverDisableCommands != null) {
|
||||
disableCommands.addAll(serverDisableCommands);
|
||||
@@ -303,8 +304,15 @@ public final class SettingsProxy extends Settings {
|
||||
return disableCommands;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
MySQL getMySQL() {
|
||||
return mySQL;
|
||||
public @Nullable RedisHandler redisHandler() {
|
||||
return redisHandler;
|
||||
}
|
||||
|
||||
private static String normalizeServerMode(@Nullable final Object mode) {
|
||||
if (mode == null) {
|
||||
return "default";
|
||||
}
|
||||
final String value = String.valueOf(mode).toLowerCase(Locale.ROOT);
|
||||
return value.isBlank() ? "default" : value;
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -26,6 +26,7 @@ import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public abstract class MaintenanceProxyCommand extends MaintenanceCommand {
|
||||
private final MaintenanceProxyPlugin plugin;
|
||||
@@ -40,7 +41,7 @@ public abstract class MaintenanceProxyCommand extends MaintenanceCommand {
|
||||
@Override
|
||||
protected void addToggleAndTimerCommands() {
|
||||
add(new SingleToggleCommand(plugin), "on", "off");
|
||||
add(new StatusCommand(plugin), "status");
|
||||
add(new ProxyStatusCommand(plugin), "status");
|
||||
|
||||
add(new SingleStarttimerCommand(plugin), "starttimer", "start");
|
||||
add(new SingleEndtimerCommand(plugin), "endtimer", "end");
|
||||
@@ -52,7 +53,7 @@ public abstract class MaintenanceProxyCommand extends MaintenanceCommand {
|
||||
public List<String> getMaintenanceServersCompletion(final String s) {
|
||||
final List<String> list = new ArrayList<>();
|
||||
for (final String server : settingsBungee.getMaintenanceServers()) {
|
||||
if (server.toLowerCase().startsWith(s)) {
|
||||
if (server.toLowerCase(Locale.ROOT).startsWith(s)) {
|
||||
list.add(server);
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -20,6 +20,8 @@ package eu.kennytv.maintenance.core.proxy.command;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.SettingsProxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ProxyCommandInfo extends CommandInfo {
|
||||
protected final MaintenanceProxyPlugin plugin;
|
||||
@@ -33,4 +35,10 @@ public abstract class ProxyCommandInfo extends CommandInfo {
|
||||
protected SettingsProxy getSettings() {
|
||||
return plugin.getSettingsProxy();
|
||||
}
|
||||
|
||||
protected List<String> getModesCompletion() {
|
||||
final List<String> modes = new ArrayList<>(getSettings().getPingMessages().getKeys());
|
||||
modes.remove("default");
|
||||
return modes;
|
||||
}
|
||||
}
|
||||
+14
-1
@@ -22,8 +22,10 @@ import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.command.ProxyCommandInfo;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class AbortSingleTimerCommand extends ProxyCommandInfo {
|
||||
|
||||
@@ -67,6 +69,17 @@ public final class AbortSingleTimerCommand extends ProxyCommandInfo {
|
||||
|
||||
@Override
|
||||
public List<String> getTabCompletion(final SenderInfo sender, final String[] args) {
|
||||
return args.length == 2 && sender.hasMaintenancePermission("singleserver.timer") ? plugin.getCommandManager().getServersCompletion(args[1]) : Collections.emptyList();
|
||||
if (args.length != 2 || !sender.hasMaintenancePermission("singleserver.timer")) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final String prefix = args[1].toLowerCase(Locale.ROOT);
|
||||
final List<String> list = new ArrayList<>();
|
||||
for (final String server : plugin.getServerTasks().keySet()) {
|
||||
if (server.toLowerCase(Locale.ROOT).startsWith(prefix)) {
|
||||
list.add(server);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.proxy.command.subcommand;
|
||||
|
||||
import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.command.ProxyCommandInfo;
|
||||
import eu.kennytv.maintenance.core.runnable.MaintenanceRunnable;
|
||||
import eu.kennytv.maintenance.core.runnable.MaintenanceRunnableBase;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import java.util.Map;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public final class ProxyStatusCommand extends ProxyCommandInfo {
|
||||
|
||||
public ProxyStatusCommand(final MaintenanceProxyPlugin plugin) {
|
||||
super(plugin, "singleserver.status");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
final MaintenanceRunnable task = plugin.getCurrentTask();
|
||||
if (task != null) {
|
||||
final String key = task.shouldEnable() ? "maintenanceStatusOffWithStartTimer" : "maintenanceStatusOnWithEndTimer";
|
||||
sender.send(getMessage(key, "%TIME%", task.getTime()));
|
||||
} else {
|
||||
sender.send(getMessage(plugin.isMaintenance() ? "maintenanceStatusOn" : "maintenanceStatusOff"));
|
||||
}
|
||||
|
||||
if (getSettings().getMaintenanceServers().isEmpty() && !plugin.hasServerTasks()) {
|
||||
sender.send(getMessage("singleServerMaintenanceListEmpty"));
|
||||
return;
|
||||
}
|
||||
|
||||
sender.send(getMessage("singleServerMaintenanceList"));
|
||||
// Enabled servers first
|
||||
for (final String server : getSettings().getMaintenanceServers()) {
|
||||
final MaintenanceRunnableBase serverTask = plugin.getServerTask(server);
|
||||
if (serverTask != null) {
|
||||
sender.send(getMessage("singleServerMaintenanceListEntryWithEndTimer", "%SERVER%", server, "%TIME%", serverTask.getTime()));
|
||||
continue;
|
||||
}
|
||||
|
||||
sender.send(getMessage("singleServerMaintenanceListEntry", "%SERVER%", server));
|
||||
}
|
||||
|
||||
// Then enabling tasks
|
||||
for (final Map.Entry<String, MaintenanceRunnableBase> entry : plugin.getServerTasks().entrySet()) {
|
||||
final MaintenanceRunnableBase serverTask = entry.getValue();
|
||||
if (serverTask.shouldEnable()) {
|
||||
sender.send(getMessage("singleServerMaintenanceListEntryWithStartTimer", "%SERVER%", entry.getKey(), "%TIME%", serverTask.getTime()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getHelpMessage() {
|
||||
return getMessage("helpStatus");
|
||||
}
|
||||
}
|
||||
+72
-35
@@ -21,10 +21,14 @@ import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.command.ProxyCommandInfo;
|
||||
import eu.kennytv.maintenance.core.runnable.MaintenanceRunnableBase;
|
||||
import eu.kennytv.maintenance.core.util.DiscordWebhook;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public final class SingleEndtimerCommand extends ProxyCommandInfo {
|
||||
|
||||
@@ -40,49 +44,82 @@ public final class SingleEndtimerCommand extends ProxyCommandInfo {
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (args.length == 2) {
|
||||
if (checkPermission(sender, "timer")) return;
|
||||
|
||||
final Duration duration = plugin.getCommandManager().parseDurationAndCheckTask(sender, args[1]);
|
||||
if (duration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
if (!plugin.isMaintenance()) {
|
||||
sender.send(getMessage("alreadyDisabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.startMaintenanceRunnable(duration, false);
|
||||
sender.send(getMessage("endtimerStarted", "%TIME%", plugin.getRunnable().getTime()));
|
||||
endGlobal(sender, args[1]);
|
||||
} else if (args.length == 3) {
|
||||
if (checkPermission(sender, "singleserver.timer")) return;
|
||||
|
||||
final Duration duration = plugin.getCommandManager().parseDurationAndCheckTask(sender, args[2], false);
|
||||
if (duration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
if (args[1].equalsIgnoreCase("global")) {
|
||||
endGlobal(sender, args[2]);
|
||||
} else {
|
||||
endSingle(sender, args[1], args[2]);
|
||||
}
|
||||
|
||||
final Server server = plugin.getCommandManager().checkSingleTimerServerArg(sender, args[1]);
|
||||
if (server == null) return;
|
||||
if (!plugin.isMaintenance(server)) {
|
||||
sender.send(getMessage("singleServerAlreadyDisabled", "%SERVER%", server.getName()));
|
||||
return;
|
||||
}
|
||||
|
||||
final MaintenanceRunnableBase runnable = plugin.startSingleMaintenanceRunnable(server, duration, false);
|
||||
sender.send(getMessage(
|
||||
"singleEndtimerStarted",
|
||||
"%TIME%", runnable.getTime(),
|
||||
"%SERVER%", server.getName()
|
||||
));
|
||||
} else {
|
||||
sender.send(getHelpMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void endGlobal(final SenderInfo sender, final String durationArg) {
|
||||
if (checkPermission(sender, "timer")) return;
|
||||
|
||||
final Duration duration = plugin.getCommandManager().parseDurationAndCheckTask(sender, durationArg);
|
||||
if (duration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
if (!plugin.isMaintenance()) {
|
||||
sender.send(getMessage("alreadyDisabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.startMaintenanceRunnable(duration, false);
|
||||
|
||||
final Component message = getMessage("endtimerStarted", "%TIME%", plugin.getRunnable().getTime());
|
||||
sender.send(message);
|
||||
plugin.sendWebhookMessage("webhookEndtimerStarted", DiscordWebhook.EventType.ENDTIMER_STARTED,
|
||||
"%TIME%", plugin.getRunnable().getTime(),
|
||||
"%TIMESTAMP%", plugin.getTargetTimestamp(duration));
|
||||
}
|
||||
|
||||
private void endSingle(final SenderInfo sender, final String serverArg, final String durationArg) {
|
||||
if (checkPermission(sender, "singleserver.timer")) return;
|
||||
|
||||
final Duration duration = plugin.getCommandManager().parseDurationAndCheckTask(sender, durationArg, false);
|
||||
if (duration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final Server server = plugin.getCommandManager().checkSingleTimerServerArg(sender, serverArg);
|
||||
if (server == null) return;
|
||||
if (!plugin.isMaintenance(server)) {
|
||||
sender.send(getMessage("singleServerAlreadyDisabled", "%SERVER%", server.getName()));
|
||||
return;
|
||||
}
|
||||
|
||||
final MaintenanceRunnableBase runnable = plugin.startSingleMaintenanceRunnable(server, duration, false);
|
||||
final Component message = getMessage(
|
||||
"singleEndtimerStarted",
|
||||
"%TIME%", runnable.getTime(),
|
||||
"%SERVER%", server.getName()
|
||||
);
|
||||
sender.send(message);
|
||||
plugin.sendWebhookMessage("webhookSingleEndtimerStarted", DiscordWebhook.EventType.ENDTIMER_STARTED,
|
||||
"%TIME%", runnable.getTime(),
|
||||
"%SERVER%", server.getName(),
|
||||
"%TIMESTAMP%", plugin.getTargetTimestamp(duration));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabCompletion(final SenderInfo sender, final String[] args) {
|
||||
return args.length == 2 && sender.hasMaintenancePermission("singleserver.timer") ? plugin.getCommandManager().getMaintenanceServersCompletion(args[1].toLowerCase()) : Collections.emptyList();
|
||||
if (args.length != 2) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final List<String> suggestions = new ArrayList<>();
|
||||
if (sender.hasMaintenancePermission("timer") && plugin.isMaintenance()) {
|
||||
suggestions.add("global");
|
||||
}
|
||||
if (sender.hasMaintenancePermission("singleserver.timer")) {
|
||||
suggestions.addAll(plugin.getCommandManager().getMaintenanceServersCompletion(args[1].toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
}
|
||||
+80
-43
@@ -23,8 +23,11 @@ import eu.kennytv.maintenance.core.proxy.command.ProxyCommandInfo;
|
||||
import eu.kennytv.maintenance.core.runnable.MaintenanceRunnableBase;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class SingleScheduleTimerCommand extends ProxyCommandInfo {
|
||||
|
||||
@@ -40,56 +43,90 @@ public final class SingleScheduleTimerCommand extends ProxyCommandInfo {
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (args.length == 3) {
|
||||
if (checkPermission(sender, "timer")) return;
|
||||
|
||||
final Duration enableIn = plugin.getCommandManager().parseDurationAndCheckTask(sender, args[1]);
|
||||
final Duration duration = plugin.getCommandManager().parseDurationAndCheckTask(sender, args[2], false);
|
||||
if (enableIn == null || duration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
scheduleGlobal(sender, null, args[1], args[2]);
|
||||
} else if (args.length == 4 || args.length == 5) {
|
||||
final String mode = args.length == 5 ? args[2] : null;
|
||||
final String enableInArg = args.length == 5 ? args[3] : args[2];
|
||||
final String durationArg = args.length == 5 ? args[4] : args[3];
|
||||
if (args[1].equalsIgnoreCase("global")) {
|
||||
scheduleGlobal(sender, mode, enableInArg, durationArg);
|
||||
} else {
|
||||
scheduleSingle(sender, args[1], mode, enableInArg, durationArg);
|
||||
}
|
||||
if (plugin.isMaintenance()) {
|
||||
sender.send(getMessage("alreadyEnabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.scheduleMaintenanceRunnable(duration, enableIn);
|
||||
sender.send(getMessage(
|
||||
"scheduletimerStarted",
|
||||
"%TIME%", plugin.getRunnable().getTime(),
|
||||
"%DURATION%", plugin.getFormattedTime((int) duration.getSeconds())
|
||||
));
|
||||
} else if (args.length == 4) {
|
||||
if (checkPermission(sender, "singleserver.timer")) return;
|
||||
|
||||
final Duration enableIn = plugin.getCommandManager().parseDurationAndCheckTask(sender, args[2], false);
|
||||
final Duration duration = plugin.getCommandManager().parseDurationAndCheckTask(sender, args[3], false);
|
||||
if (enableIn == null || duration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final Server server = plugin.getCommandManager().checkSingleTimerServerArg(sender, args[1]);
|
||||
if (server == null) return;
|
||||
if (plugin.isMaintenance(server)) {
|
||||
sender.send(getMessage("singleServerAlreadyEnabled", "%SERVER%", server.getName()));
|
||||
return;
|
||||
}
|
||||
|
||||
final MaintenanceRunnableBase runnable = plugin.scheduleSingleMaintenanceRunnable(server, enableIn, duration);
|
||||
sender.send(getMessage(
|
||||
"singleScheduletimerStarted",
|
||||
"%TIME%", runnable.getTime(),
|
||||
"%DURATION%", plugin.getFormattedTime((int) duration.getSeconds()),
|
||||
"%SERVER%", server.getName()
|
||||
));
|
||||
} else {
|
||||
sender.send(getHelpMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleGlobal(final SenderInfo sender, @Nullable final String mode, final String enableInArg, final String durationArg) {
|
||||
if (checkPermission(sender, "timer")) return;
|
||||
|
||||
final Duration enableIn = plugin.getCommandManager().parseDurationAndCheckTask(sender, enableInArg);
|
||||
final Duration duration = plugin.getCommandManager().parseDurationAndCheckTask(sender, durationArg, false);
|
||||
if (enableIn == null || duration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
if (plugin.isMaintenance()) {
|
||||
sender.send(getMessage("alreadyEnabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.scheduleMaintenanceRunnable(enableIn, duration, mode);
|
||||
sender.send(getMessage(
|
||||
"scheduletimerStarted",
|
||||
"%TIME%", plugin.getRunnable().getTime(),
|
||||
"%DURATION%", plugin.getFormattedTime((int) duration.getSeconds())
|
||||
));
|
||||
}
|
||||
|
||||
private void scheduleSingle(final SenderInfo sender, final String serverArg, @Nullable final String mode,
|
||||
final String enableInArg, final String durationArg) {
|
||||
if (checkPermission(sender, "singleserver.timer")) return;
|
||||
|
||||
final Duration enableIn = plugin.getCommandManager().parseDurationAndCheckTask(sender, enableInArg, false);
|
||||
final Duration duration = plugin.getCommandManager().parseDurationAndCheckTask(sender, durationArg, false);
|
||||
if (enableIn == null || duration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final Server server = plugin.getCommandManager().checkSingleTimerServerArg(sender, serverArg);
|
||||
if (server == null) return;
|
||||
if (plugin.isMaintenance(server)) {
|
||||
sender.send(getMessage("singleServerAlreadyEnabled", "%SERVER%", server.getName()));
|
||||
return;
|
||||
}
|
||||
|
||||
final MaintenanceRunnableBase runnable = plugin.scheduleSingleMaintenanceRunnable(server, enableIn, duration, mode);
|
||||
sender.send(getMessage(
|
||||
"singleScheduletimerStarted",
|
||||
"%TIME%", runnable.getTime(),
|
||||
"%DURATION%", plugin.getFormattedTime((int) duration.getSeconds()),
|
||||
"%SERVER%", server.getName()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabCompletion(final SenderInfo sender, final String[] args) {
|
||||
return args.length == 2 && sender.hasMaintenancePermission("singleserver.timer") ? plugin.getCommandManager().getServersCompletion(args[1].toLowerCase()) : Collections.emptyList();
|
||||
if (args.length == 2) {
|
||||
final List<String> suggestions = new ArrayList<>();
|
||||
if (sender.hasMaintenancePermission("timer")) {
|
||||
suggestions.add("global");
|
||||
}
|
||||
if (sender.hasMaintenancePermission("singleserver.timer")) {
|
||||
suggestions.addAll(plugin.getCommandManager().getServersCompletion(args[1].toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
if (args.length == 3) {
|
||||
if (args[1].equalsIgnoreCase("global")) {
|
||||
return sender.hasMaintenancePermission("timer") ? getModesCompletion() : Collections.emptyList();
|
||||
}
|
||||
if (sender.hasMaintenancePermission("singleserver.timer") && plugin.getServer(args[1]) != null) {
|
||||
return getModesCompletion();
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
+82
-36
@@ -21,10 +21,15 @@ import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.command.ProxyCommandInfo;
|
||||
import eu.kennytv.maintenance.core.runnable.MaintenanceRunnableBase;
|
||||
import eu.kennytv.maintenance.core.util.DiscordWebhook;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class SingleStarttimerCommand extends ProxyCommandInfo {
|
||||
|
||||
@@ -40,49 +45,90 @@ public final class SingleStarttimerCommand extends ProxyCommandInfo {
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (args.length == 2) {
|
||||
if (checkPermission(sender, "timer")) return;
|
||||
|
||||
final Duration duration = plugin.getCommandManager().parseDurationAndCheckTask(sender, args[1]);
|
||||
if (duration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
startGlobal(sender, null, args[1]);
|
||||
} else if (args.length == 3 || args.length == 4) {
|
||||
final String mode = args.length == 4 ? args[2] : null;
|
||||
final String durationArg = args.length == 4 ? args[3] : args[2];
|
||||
if (args[1].equalsIgnoreCase("global")) {
|
||||
startGlobal(sender, mode, durationArg);
|
||||
} else {
|
||||
startSingle(sender, args[1], mode, durationArg);
|
||||
}
|
||||
if (plugin.isMaintenance()) {
|
||||
sender.send(getMessage("alreadyEnabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.startMaintenanceRunnable(duration, true);
|
||||
sender.send(getMessage("starttimerStarted", "%TIME%", plugin.getRunnable().getTime()));
|
||||
} else if (args.length == 3) {
|
||||
if (checkPermission(sender, "singleserver.timer")) return;
|
||||
|
||||
final Duration duration = plugin.getCommandManager().parseDurationAndCheckTask(sender, args[2], false);
|
||||
if (duration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final Server server = plugin.getCommandManager().checkSingleTimerServerArg(sender, args[1]);
|
||||
if (server == null) return;
|
||||
if (plugin.isMaintenance(server)) {
|
||||
sender.send(getMessage("singleServerAlreadyEnabled", "%SERVER%", server.getName()));
|
||||
return;
|
||||
}
|
||||
|
||||
final MaintenanceRunnableBase runnable = plugin.startSingleMaintenanceRunnable(server, duration, true);
|
||||
sender.send(getMessage(
|
||||
"singleStarttimerStarted",
|
||||
"%TIME%", runnable.getTime(),
|
||||
"%SERVER%", server.getName()
|
||||
));
|
||||
} else {
|
||||
sender.send(getHelpMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void startGlobal(final SenderInfo sender, @Nullable final String mode, final String durationArg) {
|
||||
if (checkPermission(sender, "timer")) return;
|
||||
|
||||
final Duration duration = plugin.getCommandManager().parseDurationAndCheckTask(sender, durationArg);
|
||||
if (duration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
if (plugin.isMaintenance()) {
|
||||
sender.send(getMessage("alreadyEnabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.startMaintenanceRunnable(duration, true, mode);
|
||||
final Component message = getMessage("starttimerStarted", "%TIME%", plugin.getRunnable().getTime());
|
||||
sender.send(message);
|
||||
plugin.sendWebhookMessage("webhookStarttimerStarted", DiscordWebhook.EventType.STARTTIMER_STARTED,
|
||||
"%TIME%", plugin.getRunnable().getTime(),
|
||||
"%TIMESTAMP%", plugin.getTargetTimestamp(duration));
|
||||
}
|
||||
|
||||
private void startSingle(final SenderInfo sender, final String serverArg, @Nullable final String mode, final String durationArg) {
|
||||
if (checkPermission(sender, "singleserver.timer")) return;
|
||||
|
||||
final Duration duration = plugin.getCommandManager().parseDurationAndCheckTask(sender, durationArg, false);
|
||||
if (duration == null) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final Server server = plugin.getCommandManager().checkSingleTimerServerArg(sender, serverArg);
|
||||
if (server == null) return;
|
||||
if (plugin.isMaintenance(server)) {
|
||||
sender.send(getMessage("singleServerAlreadyEnabled", "%SERVER%", server.getName()));
|
||||
return;
|
||||
}
|
||||
|
||||
final MaintenanceRunnableBase runnable = plugin.startSingleMaintenanceRunnable(server, duration, true, mode);
|
||||
final Component message = getMessage(
|
||||
"singleStarttimerStarted",
|
||||
"%TIME%", runnable.getTime(),
|
||||
"%SERVER%", server.getName()
|
||||
);
|
||||
sender.send(message);
|
||||
plugin.sendWebhookMessage("webhookSingleStarttimerStarted", DiscordWebhook.EventType.STARTTIMER_STARTED,
|
||||
"%TIME%", runnable.getTime(),
|
||||
"%SERVER%", server.getName(),
|
||||
"%TIMESTAMP%", plugin.getTargetTimestamp(duration));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabCompletion(final SenderInfo sender, final String[] args) {
|
||||
return args.length == 2 && sender.hasMaintenancePermission("singleserver.timer") ? plugin.getCommandManager().getServersCompletion(args[1].toLowerCase()) : Collections.emptyList();
|
||||
if (args.length == 2) {
|
||||
final List<String> suggestions = new ArrayList<>();
|
||||
if (sender.hasMaintenancePermission("timer")) {
|
||||
suggestions.add("global");
|
||||
}
|
||||
if (sender.hasMaintenancePermission("singleserver.timer")) {
|
||||
suggestions.addAll(plugin.getCommandManager().getServersCompletion(args[1].toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
if (args.length == 3) {
|
||||
if (args[1].equalsIgnoreCase("global")) {
|
||||
return sender.hasMaintenancePermission("timer") ? getModesCompletion() : Collections.emptyList();
|
||||
}
|
||||
if (sender.hasMaintenancePermission("singleserver.timer") && plugin.getServer(args[1]) != null) {
|
||||
return getModesCompletion();
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
+106
-32
@@ -17,14 +17,15 @@
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.proxy.command.subcommand;
|
||||
|
||||
import eu.kennytv.maintenance.api.proxy.DummyServer;
|
||||
import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.command.ProxyCommandInfo;
|
||||
import eu.kennytv.maintenance.core.proxy.server.DummyServer;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class SingleToggleCommand extends ProxyCommandInfo {
|
||||
|
||||
@@ -39,48 +40,121 @@ public final class SingleToggleCommand extends ProxyCommandInfo {
|
||||
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (args.length == 0 || args.length > 3) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean maintenance = args[0].equalsIgnoreCase("on");
|
||||
if (args.length == 1) {
|
||||
if (checkPermission(sender, "toggle")) return;
|
||||
final boolean maintenance = args[0].equalsIgnoreCase("on");
|
||||
if (maintenance == plugin.isMaintenance()) {
|
||||
sender.send(getMessage(maintenance ? "alreadyEnabled" : "alreadyDisabled"));
|
||||
toggleGlobal(sender, maintenance, null);
|
||||
return;
|
||||
}
|
||||
|
||||
final String server = args[1];
|
||||
final String mode = args.length == 3 ? args[2] : null;
|
||||
if (server.equalsIgnoreCase("global")) {
|
||||
if (!maintenance && args.length == 3) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.setMaintenance(maintenance);
|
||||
} else if (args.length == 2) {
|
||||
if (checkPermission(sender, "singleserver.toggle")) return;
|
||||
final boolean maintenance = args[0].equalsIgnoreCase("on");
|
||||
Server server = plugin.getServer(args[1]);
|
||||
if (server == null) {
|
||||
if (maintenance) {
|
||||
sender.send(getMessage("serverNotFound"));
|
||||
return;
|
||||
}
|
||||
// Let servers be removed from the maintenance list, even if they don't exist on the proxy
|
||||
server = new DummyServer(args[1]);
|
||||
if (!plugin.isMaintenance(server)) {
|
||||
sender.send(getMessage("serverNotFound"));
|
||||
return;
|
||||
}
|
||||
toggleGlobal(sender, maintenance, mode);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!maintenance && args.length != 2) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
toggleServer(sender, maintenance, server, mode);
|
||||
}
|
||||
|
||||
private void toggleGlobal(final SenderInfo sender, final boolean maintenance, final String mode) {
|
||||
if (checkPermission(sender, "toggle")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (maintenance && plugin.isMaintenance()) {
|
||||
if (mode != null) {
|
||||
plugin.getSettings().setActiveMode(mode);
|
||||
return;
|
||||
}
|
||||
|
||||
if (plugin.setMaintenanceToServer(server, maintenance)) {
|
||||
if (!sender.isPlayer() || !server.getName().equals(plugin.getServerNameOf(sender))) {
|
||||
sender.send(getMessage(maintenance ? "singleMaintenanceActivated" : "singleMaintenanceDeactivated", "%SERVER%", server.getName()));
|
||||
}
|
||||
} else {
|
||||
sender.send(getMessage(maintenance ? "singleServerAlreadyEnabled" : "singleServerAlreadyDisabled", "%SERVER%", server.getName()));
|
||||
sender.send(getMessage("alreadyEnabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (maintenance == plugin.isMaintenance()) {
|
||||
sender.send(getMessage(maintenance ? "alreadyEnabled" : "alreadyDisabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.setMaintenance(maintenance, mode);
|
||||
}
|
||||
|
||||
private void toggleServer(final SenderInfo sender, final boolean maintenance, final String serverArg, final String mode) {
|
||||
if (checkPermission(sender, "singleserver.toggle")) {
|
||||
return;
|
||||
}
|
||||
|
||||
Server server = plugin.getServer(serverArg);
|
||||
if (server == null) {
|
||||
if (maintenance) {
|
||||
sender.send(getMessage("serverNotFound"));
|
||||
return;
|
||||
}
|
||||
// Let servers be removed from the maintenance list, even if they don't exist on the proxy
|
||||
server = new DummyServer(serverArg);
|
||||
if (!plugin.isMaintenance(server)) {
|
||||
sender.send(getMessage("serverNotFound"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (maintenance && plugin.isMaintenance(server) && mode != null) {
|
||||
if (plugin.setMaintenanceModeToServer(server, mode)) {
|
||||
return;
|
||||
}
|
||||
sender.send(getMessage("singleServerAlreadyEnabled", "%SERVER%", server.getName()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (plugin.setMaintenanceToServer(server, maintenance, mode)) {
|
||||
if (!sender.isPlayer() || !server.getName().equals(plugin.getServerNameOf(sender))) {
|
||||
sender.send(getMessage(maintenance ? "singleMaintenanceActivated" : "singleMaintenanceDeactivated", "%SERVER%", server.getName()));
|
||||
}
|
||||
} else {
|
||||
sender.send(getHelpMessage());
|
||||
sender.send(getMessage(maintenance ? "singleServerAlreadyEnabled" : "singleServerAlreadyDisabled", "%SERVER%", server.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabCompletion(final SenderInfo sender, final String[] args) {
|
||||
if (args.length != 2 || !sender.hasMaintenancePermission("singleserver.toggle")) return Collections.emptyList();
|
||||
return args[0].equalsIgnoreCase("off") ? plugin.getCommandManager().getMaintenanceServersCompletion(args[1].toLowerCase())
|
||||
: plugin.getCommandManager().getServersCompletion(args[1].toLowerCase());
|
||||
if (args.length == 2) {
|
||||
final List<String> suggestions = new ArrayList<>();
|
||||
if (sender.hasMaintenancePermission("toggle") && args[0].equalsIgnoreCase("on")) {
|
||||
suggestions.add("global");
|
||||
}
|
||||
|
||||
if (!sender.hasMaintenancePermission("singleserver.toggle")) {
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
final List<String> serverSuggestions = args[0].equalsIgnoreCase("off")
|
||||
? plugin.getCommandManager().getMaintenanceServersCompletion(args[1].toLowerCase(Locale.ROOT))
|
||||
: plugin.getCommandManager().getServersCompletion(args[1].toLowerCase(Locale.ROOT));
|
||||
suggestions.addAll(serverSuggestions);
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
if (args.length == 3 && args[0].equalsIgnoreCase("on") && (sender.hasMaintenancePermission("toggle") || sender.hasMaintenancePermission("singleserver.toggle"))) {
|
||||
final List<String> modes = new ArrayList<>(getSettings().getPingMessages().getKeys());
|
||||
modes.remove("default");
|
||||
return modes;
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
+5
-4
@@ -24,6 +24,7 @@ import eu.kennytv.maintenance.core.proxy.SettingsProxy;
|
||||
import eu.kennytv.maintenance.core.proxy.util.ProxySenderInfo;
|
||||
import eu.kennytv.maintenance.core.proxy.util.ServerConnectResult;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import java.util.Locale;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class ProxyJoinListenerBase extends JoinListenerBase {
|
||||
@@ -48,7 +49,7 @@ public abstract class ProxyJoinListenerBase extends JoinListenerBase {
|
||||
protected ServerConnectResult serverConnect(final ProxySenderInfo sender, final Server target, final boolean normalServerConnect) {
|
||||
// Check waiting server for global maintenance
|
||||
if (settings.isMaintenance()) {
|
||||
if (sender.hasMaintenancePermission("bypass") || settings.isWhitelisted(sender.getUuid())) return ALLOWED;
|
||||
if (sender.hasMaintenancePermission("bypass") || settings.isWhitelisted(sender.uuid())) return ALLOWED;
|
||||
|
||||
final Server waitingServer = shouldConnectToWaitingServer(sender);
|
||||
// Should never be null, but just in case
|
||||
@@ -70,13 +71,13 @@ public abstract class ProxyJoinListenerBase extends JoinListenerBase {
|
||||
|
||||
// Single server maintenance
|
||||
if (!settings.isMaintenance(target.getName())) return ALLOWED;
|
||||
if (sender.hasMaintenancePermission("bypass") || settings.isWhitelisted(sender.getUuid())
|
||||
|| sender.hasMaintenancePermission("singleserver.bypass." + target.getName().toLowerCase())) {
|
||||
if (sender.hasMaintenancePermission("bypass") || settings.isWhitelisted(sender.uuid())
|
||||
|| sender.hasMaintenancePermission("singleserver.bypass." + target.getName().toLowerCase(Locale.ROOT))) {
|
||||
return ALLOWED;
|
||||
}
|
||||
|
||||
if (settings.isJoinNotifications()) {
|
||||
broadcastJoinNotification(sender.getName(), target);
|
||||
broadcastJoinNotification(sender.name(), target);
|
||||
}
|
||||
|
||||
if (normalServerConnect) {
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.proxy.mysql;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public final class MySQL {
|
||||
private final Logger logger;
|
||||
private final HikariDataSource hikariDataSource;
|
||||
|
||||
public MySQL(final Logger logger, final String hostname, final int port, final String username, final String password, final String database, final boolean useSSL) {
|
||||
this.logger = logger;
|
||||
|
||||
final HikariConfig hikariConfig = new HikariConfig();
|
||||
hikariConfig.setMaximumPoolSize(10);
|
||||
hikariConfig.addDataSourceProperty("serverName", hostname);
|
||||
hikariConfig.addDataSourceProperty("user", username);
|
||||
hikariConfig.addDataSourceProperty("password", password);
|
||||
|
||||
String urlProperty = "jdbc:mysql://" + hostname + ":" + port + "/" + database;
|
||||
if (!useSSL) {
|
||||
urlProperty += "?useSSL=false";
|
||||
}
|
||||
hikariConfig.addDataSourceProperty("url", urlProperty);
|
||||
|
||||
hikariConfig.setJdbcUrl(urlProperty);
|
||||
hikariConfig.setDataSourceClassName(findDriver("com.mysql.jdbc.jdbc2.optional.MysqlDataSource", "com.mysql.cj.jdbc.MysqlDataSource", "org.mariadb.jdbc.MariaDbDataSource"));
|
||||
hikariDataSource = new HikariDataSource(hikariConfig);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String findDriver(final String... classNames) {
|
||||
for (final String name : classNames) {
|
||||
try {
|
||||
Class.forName(name);
|
||||
return name;
|
||||
} catch (final ClassNotFoundException ignored) {
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No sql driver class found");
|
||||
}
|
||||
|
||||
public void executeUpdate(final String query, final Consumer<Integer> callback, final Object... objects) {
|
||||
try (final Connection connection = hikariDataSource.getConnection()) {
|
||||
try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
|
||||
int current = 1;
|
||||
for (final Object object : objects) {
|
||||
preparedStatement.setObject(current, object);
|
||||
current++;
|
||||
}
|
||||
|
||||
callback.accept(preparedStatement.executeUpdate());
|
||||
}
|
||||
} catch (final SQLException e) {
|
||||
logger.log(Level.SEVERE, "Error while executing update method: " + query);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void executeQuery(final String query, final Consumer<ResultSet> callback, final Object... objects) {
|
||||
try (final Connection connection = hikariDataSource.getConnection()) {
|
||||
try (final PreparedStatement preparedStatement = connection.prepareStatement(query)) {
|
||||
int current = 1;
|
||||
for (final Object object : objects) {
|
||||
preparedStatement.setObject(current, object);
|
||||
current++;
|
||||
}
|
||||
|
||||
try (final ResultSet resultSet = preparedStatement.executeQuery()) {
|
||||
callback.accept(resultSet);
|
||||
}
|
||||
}
|
||||
} catch (final SQLException e) {
|
||||
logger.log(Level.SEVERE, "Error while executing query method: " + query);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void executeUpdate(final String query, final Object... objects) {
|
||||
executeUpdate(query, res -> {
|
||||
}, objects);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
hikariDataSource.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
package eu.kennytv.maintenance.core.proxy.redis;
|
||||
|
||||
import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.SettingsProxy;
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Level;
|
||||
import java.util.Locale;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import redis.clients.jedis.ConnectionPoolConfig;
|
||||
import redis.clients.jedis.RedisClient;
|
||||
import redis.clients.jedis.StreamEntryID;
|
||||
import redis.clients.jedis.params.XReadParams;
|
||||
import redis.clients.jedis.resps.StreamEntry;
|
||||
|
||||
public final class RedisHandler {
|
||||
|
||||
private static final String REDIS_STREAM_KEY = "maintenance:stream";
|
||||
private static final String REDIS_MAINTENANCE_KEY = "maintenance:status";
|
||||
private static final String REDIS_MODE_KEY = "maintenance:mode";
|
||||
private static final String REDIS_SERVERS_KEY = "maintenance:servers";
|
||||
private static final String REDIS_SERVER_MODES_KEY = "maintenance:server_modes";
|
||||
private static final String REDIS_WHITELIST_KEY = "maintenance:whitelist";
|
||||
|
||||
private static final String MSG_TYPE_MAINTENANCE_UPDATE = "status";
|
||||
private static final String MSG_TYPE_SERVER_ADD = "server_add";
|
||||
private static final String MSG_TYPE_SERVER_REMOVE = "server_remove";
|
||||
private static final String MSG_TYPE_PLAYER_ADD = "player_add";
|
||||
private static final String MSG_TYPE_PLAYER_REMOVE = "player_remove";
|
||||
private static final String MSG_TYPE = "type";
|
||||
private static final String MSG_VALUE = "val";
|
||||
|
||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||
private final MaintenanceProxyPlugin plugin;
|
||||
private final SettingsProxy settings;
|
||||
private RedisClient client;
|
||||
private StreamEntryID lastStreamId = new StreamEntryID();
|
||||
|
||||
public RedisHandler(final MaintenanceProxyPlugin plugin, final SettingsProxy settings) {
|
||||
this.plugin = plugin;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
public void setup(final String redisUri) {
|
||||
plugin.getLogger().info("Trying to open Redis connection...");
|
||||
|
||||
final ConnectionPoolConfig poolConfig = new ConnectionPoolConfig();
|
||||
poolConfig.setMaxWait(Duration.ofSeconds(5));
|
||||
poolConfig.setMaxTotal(2);
|
||||
client = RedisClient.builder().poolConfig(poolConfig).fromURI(URI.create(redisUri)).build();
|
||||
|
||||
// Get the latest stream ID to only receive new messages
|
||||
final List<StreamEntry> messages = client.xrevrange(REDIS_STREAM_KEY, (StreamEntryID) null, null, 1);
|
||||
if (!messages.isEmpty()) {
|
||||
lastStreamId = messages.getFirst().getID();
|
||||
}
|
||||
|
||||
running.set(true);
|
||||
plugin.async(this::listenToStream);
|
||||
|
||||
plugin.getLogger().info("Connected to Redis!");
|
||||
}
|
||||
|
||||
public boolean loadMaintenanceStatus() {
|
||||
return Boolean.parseBoolean(client.get(REDIS_MAINTENANCE_KEY));
|
||||
}
|
||||
|
||||
public @Nullable String loadGlobalMode() {
|
||||
return client.get(REDIS_MODE_KEY);
|
||||
}
|
||||
|
||||
public Map<String, String> loadMaintenanceServers() {
|
||||
final Set<String> servers = client.smembers(REDIS_SERVERS_KEY);
|
||||
final Map<String, String> serverModes = new HashMap<>();
|
||||
if (servers == null) {
|
||||
return serverModes;
|
||||
}
|
||||
|
||||
final Map<String, String> persistedModes = client.hgetAll(REDIS_SERVER_MODES_KEY);
|
||||
for (final String server : servers) {
|
||||
serverModes.put(server, normalizeMode(persistedModes.get(server)));
|
||||
}
|
||||
return serverModes;
|
||||
}
|
||||
|
||||
public void loadPlayers(final Map<UUID, String> players) {
|
||||
for (final Map.Entry<String, String> entry : client.hgetAll(REDIS_WHITELIST_KEY).entrySet()) {
|
||||
players.put(UUID.fromString(entry.getKey()), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void set(final boolean maintenance, final String mode) {
|
||||
client.set(REDIS_MAINTENANCE_KEY, Boolean.toString(maintenance));
|
||||
client.set(REDIS_MODE_KEY, mode);
|
||||
client.xadd(REDIS_STREAM_KEY, StreamEntryID.NEW_ENTRY, Map.of(
|
||||
MSG_TYPE, MSG_TYPE_MAINTENANCE_UPDATE,
|
||||
MSG_VALUE, Boolean.toString(maintenance),
|
||||
"mode", mode
|
||||
));
|
||||
}
|
||||
|
||||
public boolean addServer(final String server, final String mode) {
|
||||
if (client.sadd(REDIS_SERVERS_KEY, server) < 1) {
|
||||
return false;
|
||||
}
|
||||
client.hset(REDIS_SERVER_MODES_KEY, server, mode);
|
||||
client.xadd(REDIS_STREAM_KEY, StreamEntryID.NEW_ENTRY, Map.of(
|
||||
MSG_TYPE, MSG_TYPE_SERVER_ADD,
|
||||
MSG_VALUE, server,
|
||||
"mode", mode
|
||||
));
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setServerMode(final String server, final String mode) {
|
||||
client.hset(REDIS_SERVER_MODES_KEY, server, mode);
|
||||
client.xadd(REDIS_STREAM_KEY, StreamEntryID.NEW_ENTRY, Map.of(
|
||||
MSG_TYPE, MSG_TYPE_SERVER_ADD,
|
||||
MSG_VALUE, server,
|
||||
"mode", mode
|
||||
));
|
||||
}
|
||||
|
||||
public boolean removeServer(final String server) {
|
||||
if (client.srem(REDIS_SERVERS_KEY, server) < 1) {
|
||||
return false;
|
||||
}
|
||||
client.hdel(REDIS_SERVER_MODES_KEY, server);
|
||||
send(MSG_TYPE_SERVER_REMOVE, server);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void addPlayer(final UUID uuid, final String name) {
|
||||
final String value = uuid.toString();
|
||||
client.hset(REDIS_WHITELIST_KEY, value, name);
|
||||
client.xadd(REDIS_STREAM_KEY, StreamEntryID.NEW_ENTRY, Map.of(
|
||||
MSG_TYPE, MSG_TYPE_PLAYER_ADD,
|
||||
MSG_VALUE, value,
|
||||
"name", name
|
||||
));
|
||||
}
|
||||
|
||||
public void removePlayer(final UUID uuid) {
|
||||
final String value = uuid.toString();
|
||||
client.hdel(REDIS_WHITELIST_KEY, value);
|
||||
send(MSG_TYPE_PLAYER_REMOVE, value);
|
||||
}
|
||||
|
||||
private void send(final String type, final String value) {
|
||||
client.xadd(REDIS_STREAM_KEY, StreamEntryID.NEW_ENTRY, Map.of(MSG_TYPE, type, MSG_VALUE, value));
|
||||
}
|
||||
|
||||
private void listenToStream() {
|
||||
while (running.get()) {
|
||||
try {
|
||||
final List<Map.Entry<String, List<StreamEntry>>> result = client.xread(
|
||||
XReadParams.xReadParams().block(5000),
|
||||
Map.of(REDIS_STREAM_KEY, lastStreamId)
|
||||
);
|
||||
if (result != null) {
|
||||
for (final Map.Entry<String, List<StreamEntry>> streamEntries : result) {
|
||||
for (final StreamEntry entry : streamEntries.getValue()) {
|
||||
plugin.sync(() -> processStreamMessage(entry));
|
||||
lastStreamId = entry.getID();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
if (running.get()) {
|
||||
plugin.getLogger().log(Level.WARNING, "Error reading from Redis stream", e);
|
||||
try {
|
||||
Thread.sleep(1000); // retry
|
||||
} catch (final InterruptedException ignored) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processStreamMessage(final StreamEntry message) {
|
||||
final Map<String, String> body = message.getFields();
|
||||
final String type = body.get(MSG_TYPE);
|
||||
if (type == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String value = body.get(MSG_VALUE);
|
||||
switch (type) {
|
||||
case MSG_TYPE_MAINTENANCE_UPDATE -> handleStatus(value, body.get("mode"));
|
||||
case MSG_TYPE_SERVER_ADD -> handleServerStatus(value, body.get("mode"), true, "Server added to maintenance via Redis: ");
|
||||
case MSG_TYPE_SERVER_REMOVE -> handleServerStatus(value, null, false, "Server removed from maintenance via Redis: ");
|
||||
case MSG_TYPE_PLAYER_ADD -> handlePlayerAdd(value, body.get("name"));
|
||||
case MSG_TYPE_PLAYER_REMOVE -> handlePlayerRemove(value);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleStatus(final String value, @Nullable final String mode) {
|
||||
final boolean maintenance = Boolean.parseBoolean(value);
|
||||
if (settings.isMaintenance() != maintenance) {
|
||||
plugin.getLogger().info("Maintenance status updated via Redis: " + maintenance);
|
||||
settings.setMaintenanceDirect(maintenance, mode);
|
||||
plugin.serverActions(maintenance);
|
||||
return;
|
||||
}
|
||||
|
||||
if (maintenance && !normalizeMode(mode).equals(normalizeMode(settings.activeMode()))) {
|
||||
settings.setActiveModeDirect(mode);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleServerStatus(final String value, @Nullable final String mode, final boolean maintenance, final String log) {
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean success;
|
||||
if (maintenance) {
|
||||
success = settings.addMaintenanceServerDirect(value, mode);
|
||||
settings.setMaintenanceServerModeDirect(value, mode);
|
||||
} else {
|
||||
success = settings.removeMaintenanceServerDirect(value);
|
||||
}
|
||||
|
||||
if (success) {
|
||||
plugin.getLogger().info(log + value);
|
||||
final Server server = plugin.getServerOrDummy(value);
|
||||
plugin.serverActions(server, maintenance);
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePlayerAdd(final String uuid, final String name) {
|
||||
settings.addWhitelistedPlayerDirect(UUID.fromString(uuid), name);
|
||||
}
|
||||
|
||||
private void handlePlayerRemove(final String value) {
|
||||
settings.removeWhitelistedPlayerDirect(UUID.fromString(value));
|
||||
}
|
||||
|
||||
public void close() {
|
||||
running.set(false);
|
||||
if (client != null) {
|
||||
client.close();
|
||||
client = null;
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizeMode(@Nullable final String mode) {
|
||||
return mode == null || mode.isBlank() ? "default" : mode.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
+13
-4
@@ -17,19 +17,23 @@
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.proxy.runnable;
|
||||
|
||||
import eu.kennytv.maintenance.api.proxy.MaintenanceProxy;
|
||||
import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.Settings;
|
||||
import eu.kennytv.maintenance.core.runnable.MaintenanceRunnableBase;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SingleMaintenanceRunnable extends MaintenanceRunnableBase {
|
||||
protected final Server server;
|
||||
protected final String mode;
|
||||
|
||||
public SingleMaintenanceRunnable(final MaintenancePlugin plugin, final Settings settings, final int seconds, final boolean enable, final Server server) {
|
||||
public SingleMaintenanceRunnable(final MaintenancePlugin plugin, final Settings settings, final int seconds,
|
||||
final boolean enable, final Server server, @Nullable final String mode) {
|
||||
super(plugin, settings, seconds, enable);
|
||||
this.server = server;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -39,7 +43,12 @@ public class SingleMaintenanceRunnable extends MaintenanceRunnableBase {
|
||||
|
||||
@Override
|
||||
protected void finish() {
|
||||
((MaintenanceProxy) plugin).setMaintenanceToServer(server, enable);
|
||||
final MaintenanceProxyPlugin proxyPlugin = (MaintenanceProxyPlugin) plugin;
|
||||
proxyPlugin.setMaintenanceToServer(server, enable, mode);
|
||||
// Make sure no-ops have the platform task cancelled as well
|
||||
if (proxyPlugin.getServerTasks().remove(server.getName(), this)) {
|
||||
getTask().cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
-3
@@ -21,14 +21,16 @@ import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.Settings;
|
||||
import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import java.time.Duration;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class SingleMaintenanceScheduleRunnable extends SingleMaintenanceRunnable {
|
||||
private final int maintenanceDurationSeconds;
|
||||
|
||||
public SingleMaintenanceScheduleRunnable(final MaintenancePlugin plugin, final Settings settings, final int seconds, final int maintenanceDuration, final Server server) {
|
||||
super(plugin, settings, seconds, true, server);
|
||||
public SingleMaintenanceScheduleRunnable(final MaintenancePlugin plugin, final Settings settings, final int seconds,
|
||||
final int maintenanceDuration, final Server server, @Nullable final String mode) {
|
||||
super(plugin, settings, seconds, true, server, mode);
|
||||
this.maintenanceDurationSeconds = maintenanceDuration;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,20 +19,5 @@ package eu.kennytv.maintenance.core.proxy.util;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public final class ProfileLookup {
|
||||
private final UUID uuid;
|
||||
private final String name;
|
||||
|
||||
public ProfileLookup(final UUID uuid, final String name) {
|
||||
this.uuid = uuid;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public record ProfileLookup(UUID uuid, String name) {
|
||||
}
|
||||
+2
-19
@@ -18,18 +18,11 @@
|
||||
package eu.kennytv.maintenance.core.proxy.util;
|
||||
|
||||
import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
|
||||
import java.util.UUID;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public final class ProxyOfflineSenderInfo implements ProxySenderInfo {
|
||||
private final UUID uuid;
|
||||
private final String name;
|
||||
|
||||
public ProxyOfflineSenderInfo(final UUID uuid, final String name) {
|
||||
this.uuid = uuid;
|
||||
this.name = name;
|
||||
}
|
||||
public record ProxyOfflineSenderInfo(UUID uuid, String name) implements ProxySenderInfo {
|
||||
|
||||
@Override
|
||||
public boolean canAccess(final Server server) {
|
||||
@@ -40,16 +33,6 @@ public final class ProxyOfflineSenderInfo implements ProxySenderInfo {
|
||||
public void disconnect(final Component component) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(final String permission) {
|
||||
return false;
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ package eu.kennytv.maintenance.core.proxy.util;
|
||||
|
||||
import eu.kennytv.maintenance.api.proxy.Server;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public interface ProxySenderInfo extends SenderInfo {
|
||||
|
||||
|
||||
@@ -16,9 +16,10 @@ sourceSets {
|
||||
|
||||
dependencies {
|
||||
api(projects.maintenanceApi)
|
||||
api(projects.adventure.adventureApi) {
|
||||
targetConfiguration = "shadow"
|
||||
}
|
||||
compileOnlyApi(libs.adventureApi)
|
||||
compileOnlyApi(libs.adventureTextMinimessage)
|
||||
compileOnlyApi(libs.adventureTextSerializerPlain)
|
||||
compileOnlyApi(libs.adventureTextSerializerLegacy)
|
||||
compileOnly(libs.luckperms)
|
||||
compileOnly(libs.serverlistplus)
|
||||
compileOnly(libs.guava)
|
||||
|
||||
@@ -32,12 +32,11 @@ import eu.kennytv.maintenance.core.dump.PluginDump;
|
||||
import eu.kennytv.maintenance.core.hook.ServerListPlusHook;
|
||||
import eu.kennytv.maintenance.core.runnable.MaintenanceRunnable;
|
||||
import eu.kennytv.maintenance.core.runnable.MaintenanceScheduleRunnable;
|
||||
import eu.kennytv.maintenance.core.util.DiscordWebhook;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.core.util.ServerType;
|
||||
import eu.kennytv.maintenance.core.util.Task;
|
||||
import eu.kennytv.maintenance.core.util.Version;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.format.NamedTextColor;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
@@ -47,7 +46,7 @@ import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URI;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
@@ -62,6 +61,8 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class MaintenancePlugin implements Maintenance {
|
||||
@@ -96,19 +97,9 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaintenance(final boolean maintenance) {
|
||||
settings.setMaintenance(maintenance);
|
||||
settings.getConfig().set("maintenance-enabled", maintenance);
|
||||
settings.saveConfig();
|
||||
public void setMaintenance(final boolean maintenance, final String mode) {
|
||||
settings.setMaintenance(maintenance, mode);
|
||||
serverActions(maintenance);
|
||||
|
||||
for (final String command : (maintenance ? settings.getCommandsOnMaintenanceEnable() : settings.getCommandsOnMaintenanceDisable())) {
|
||||
try {
|
||||
executeConsoleCommand(command);
|
||||
} catch (final Exception e) {
|
||||
getLogger().log(Level.SEVERE, "Error while executing extra maintenance " + (maintenance ? "enable" : "disable") + " command: " + command, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void serverActions(final boolean maintenance) {
|
||||
@@ -119,22 +110,56 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
serverListPlusHook.setEnabled(!maintenance);
|
||||
}
|
||||
|
||||
if (maintenance) {
|
||||
broadcast(settings.getMessage("maintenanceActivated"));
|
||||
if (settings.isKickOnlinePlayers()) {
|
||||
kickPlayers();
|
||||
}
|
||||
} else {
|
||||
broadcast(settings.getMessage("maintenanceDeactivated"));
|
||||
broadcast(maintenance ? settings.getMessage("maintenanceActivated") : settings.getMessage("maintenanceDeactivated"));
|
||||
if (maintenance && settings.isKickOnlinePlayers()) {
|
||||
kickPlayers();
|
||||
}
|
||||
|
||||
eventManager.callEvent(new MaintenanceChangedEvent(maintenance));
|
||||
if (maintenance) {
|
||||
sendWebhookMessage("webhookMaintenanceActivated", DiscordWebhook.EventType.MAINTENANCE_ENABLED);
|
||||
} else {
|
||||
sendWebhookMessage("webhookMaintenanceDeactivated", DiscordWebhook.EventType.MAINTENANCE_DISABLED);
|
||||
}
|
||||
|
||||
for (final String command : (maintenance ? settings.getCommandsOnMaintenanceEnable() : settings.getCommandsOnMaintenanceDisable())) {
|
||||
try {
|
||||
executeConsoleCommand(command);
|
||||
} catch (final Exception e) {
|
||||
getLogger().log(Level.SEVERE, "Error while executing extra maintenance " + (maintenance ? "enable" : "disable") + " command: " + command, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String replacePingVariables(String component) {
|
||||
public void sendWebhookMessage(final String messageKey, final DiscordWebhook.EventType eventType, final String... replacements) {
|
||||
if (!settings.isWebhookEnabled() || !settings.isWebhookEventEnabled(eventType.configKey())) {
|
||||
return;
|
||||
}
|
||||
|
||||
async(() -> {
|
||||
try {
|
||||
DiscordWebhook.sendMessage(settings.getWebhookMessage(messageKey, replacements), eventType, settings);
|
||||
} catch (final Exception e) {
|
||||
getLogger().log(Level.WARNING, "Could not send Discord webhook message", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public String getTargetTimestamp(final Duration duration) {
|
||||
return Long.toString((System.currentTimeMillis() + duration.toMillis()) / TimeUnit.SECONDS.toMillis(1));
|
||||
}
|
||||
|
||||
public String replacePingVariables(final String component) {
|
||||
return replacePingVariables(component, settings.activeMode());
|
||||
}
|
||||
|
||||
public String replacePingVariables(String component, @Nullable final String mode) {
|
||||
if (component.contains("%TIMER%")) {
|
||||
component = component.replace("%TIMER%", getTimerMessage());
|
||||
}
|
||||
final String reason = settings.activeReason() == null ? "" : settings.activeReason();
|
||||
component = component.replace("%REASON%", reason);
|
||||
component = component.replace("%MODE%", mode == null ? "default" : mode);
|
||||
component = component.replace("%ONLINE%", String.valueOf(getOnlinePlayers()));
|
||||
component = component.replace("%MAX%", String.valueOf(getMaxPlayers()));
|
||||
return component;
|
||||
@@ -168,14 +193,18 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
|
||||
private void append(final StringBuilder builder, final String timeUnit, final int time) {
|
||||
if (time == 0) return;
|
||||
if (builder.length() != 0) {
|
||||
if (!builder.isEmpty()) {
|
||||
builder.append(' ');
|
||||
}
|
||||
builder.append(time).append(' ').append(settings.language.getString(time == 1 ? timeUnit : timeUnit + "s"));
|
||||
}
|
||||
|
||||
public void startMaintenanceRunnable(final Duration duration, final boolean enable) {
|
||||
runnable = new MaintenanceRunnable(this, settings, (int) duration.getSeconds(), enable);
|
||||
startMaintenanceRunnable(duration, enable, null);
|
||||
}
|
||||
|
||||
public void startMaintenanceRunnable(final Duration duration, final boolean enable, @Nullable final String mode) {
|
||||
runnable = new MaintenanceRunnable(this, settings, (int) duration.getSeconds(), enable, mode);
|
||||
// Save the endtimer to be able to continue it after a server stop
|
||||
if (settings.isSaveEndtimerOnStop() && !runnable.shouldEnable()) {
|
||||
settings.setSavedEndtimer(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(runnable.getSecondsLeft()));
|
||||
@@ -183,7 +212,11 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
}
|
||||
|
||||
public void scheduleMaintenanceRunnable(final Duration enableIn, final Duration maintenanceDuration) {
|
||||
runnable = new MaintenanceScheduleRunnable(this, settings, (int) enableIn.getSeconds(), (int) maintenanceDuration.getSeconds());
|
||||
scheduleMaintenanceRunnable(enableIn, maintenanceDuration, null);
|
||||
}
|
||||
|
||||
public void scheduleMaintenanceRunnable(final Duration enableIn, final Duration maintenanceDuration, @Nullable final String mode) {
|
||||
runnable = new MaintenanceScheduleRunnable(this, settings, (int) enableIn.getSeconds(), (int) maintenanceDuration.getSeconds(), mode);
|
||||
}
|
||||
|
||||
public boolean updateAvailable() {
|
||||
@@ -227,7 +260,7 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
if (compare < 0) {
|
||||
getLogger().warning("Newest version available: Version " + newestVersion + ", you're on " + version);
|
||||
} else if (compare > 0) {
|
||||
if (version.getTag().equalsIgnoreCase("snapshot")) {
|
||||
if (version.getTag() != null && !version.getTag().isBlank()) {
|
||||
getLogger().info("You're running a development version, please report bugs on the Discord server (https://discord.gg/vGCUzHq) or the GitHub issue tracker (https://github.com/kennytv/Maintenance/issues)");
|
||||
} else {
|
||||
getLogger().info("You're running a version, that doesn't exist!");
|
||||
@@ -237,11 +270,9 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
}
|
||||
|
||||
public boolean installUpdate() throws Exception {
|
||||
// Sponge and Velocity need their own jar
|
||||
final String platformInfix = serverType == ServerType.VELOCITY ? "Velocity-" : serverType == ServerType.SPONGE ? "Sponge-" : "";
|
||||
final String fileName = "Maintenance-" + platformInfix + newestVersion + ".jar";
|
||||
final String fileName = "Maintenance-" + serverType.name() + "-" + newestVersion + ".jar";
|
||||
final Path tempFilePath = Paths.get(getPluginFolder() + "Maintenance.tmp");
|
||||
final URLConnection connection = new URL("https://github.com/kennytv/Maintenance/releases/download/" + newestVersion + "/" + fileName).openConnection();
|
||||
final URLConnection connection = URI.create("https://github.com/kennytv/Maintenance/releases/download/" + newestVersion + "/" + fileName).toURL().openConnection();
|
||||
try (final BufferedInputStream is = new BufferedInputStream(connection.getInputStream());
|
||||
final BufferedOutputStream os = new BufferedOutputStream(Files.newOutputStream(tempFilePath))) {
|
||||
writeFile(is, os);
|
||||
@@ -273,8 +304,10 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
}
|
||||
|
||||
private void checkNewestVersion() throws Exception {
|
||||
final URLConnection connection = new URL("https://hangar.papermc.io/api/v1/projects/Maintenance/latestrelease").openConnection();
|
||||
final URLConnection connection = URI.create("https://hangar.papermc.io/api/v1/projects/Maintenance/latestrelease").toURL().openConnection();
|
||||
connection.setRequestProperty("User-Agent", "Maintenance/" + getVersion());
|
||||
connection.setConnectTimeout(5_000);
|
||||
connection.setReadTimeout(10_000);
|
||||
try (final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
|
||||
final String newVersionString = reader.readLine();
|
||||
final Version newVersion = new Version(newVersionString);
|
||||
@@ -287,7 +320,7 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
public String pasteDump() {
|
||||
final MaintenanceDump dump = new MaintenanceDump(this, settings);
|
||||
try {
|
||||
final HttpURLConnection connection = (HttpURLConnection) new URL("https://api.pastes.dev/post").openConnection();
|
||||
final HttpURLConnection connection = (HttpURLConnection) URI.create("https://api.pastes.dev/post").toURL().openConnection();
|
||||
connection.setDoOutput(true);
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("User-Agent", "Maintenance/" + getVersion());
|
||||
@@ -328,8 +361,7 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
try {
|
||||
loadIcon(file);
|
||||
} catch (final Exception e) {
|
||||
getLogger().log(Level.WARNING, "Could not load the 'maintenance-icon.png' file!");
|
||||
e.printStackTrace();
|
||||
getLogger().log(Level.WARNING, "Could not load the 'maintenance-icon.png' file!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,6 +406,10 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
return runnable != null;
|
||||
}
|
||||
|
||||
public @Nullable MaintenanceRunnable getCurrentTask() {
|
||||
return runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Settings getSettings() {
|
||||
return settings;
|
||||
@@ -436,6 +472,10 @@ public abstract class MaintenancePlugin implements Maintenance {
|
||||
return "plugins/";
|
||||
}
|
||||
|
||||
public void sync(final Runnable runnable) {
|
||||
async(runnable);
|
||||
}
|
||||
|
||||
public abstract void async(Runnable runnable);
|
||||
|
||||
protected abstract void executeConsoleCommand(String command);
|
||||
|
||||
@@ -20,11 +20,8 @@ package eu.kennytv.maintenance.core;
|
||||
import eu.kennytv.maintenance.api.event.MaintenanceReloadedEvent;
|
||||
import eu.kennytv.maintenance.core.config.Config;
|
||||
import eu.kennytv.maintenance.core.config.ConfigSection;
|
||||
import eu.kennytv.maintenance.core.config.DisplayedMessages;
|
||||
import eu.kennytv.maintenance.core.util.ServerType;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.TextComponent;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -32,34 +29,43 @@ import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.logging.Level;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
public static final String NEW_LINE_REPLACEMENT = "<br>";
|
||||
private static final int CONFIG_VERSION = 9;
|
||||
private static final int LANGUAGE_VERSION = 2;
|
||||
private static final Random RANDOM = new Random();
|
||||
private static final int CONFIG_VERSION = 12;
|
||||
private static final int LANGUAGE_VERSION = 5;
|
||||
protected final MaintenancePlugin plugin;
|
||||
private final Map<UUID, String> whitelistedPlayers = new HashMap<>();
|
||||
private final String[] unsupportedFields;
|
||||
protected boolean maintenance;
|
||||
protected String activeMode;
|
||||
protected String activeReason;
|
||||
private Set<Integer> broadcastIntervals;
|
||||
private List<String> pingMessages;
|
||||
private List<String> timerSpecificPingMessages;
|
||||
private DisplayedMessages pingMessages;
|
||||
private DisplayedMessages timerSpecificPingMessages;
|
||||
private List<String> commandsOnMaintenanceEnable;
|
||||
private List<String> commandsOnMaintenanceDisable;
|
||||
private String legacyParsedPlayerCountMessage;
|
||||
private String legacyParsedTimerPlayerCountMessage;
|
||||
private List<String> legacyParsedPlayerCountHoverLines;
|
||||
private List<String> legacyParsedTimerPlayerCountHoverLines;
|
||||
private DisplayedMessages legacyParsedPlayerCountMessages;
|
||||
private DisplayedMessages legacyParsedTimerPlayerCountMessages;
|
||||
private DisplayedMessages legacyParsedPlayerCountHoverLines;
|
||||
private DisplayedMessages legacyParsedTimerPlayerCountHoverLines;
|
||||
private String prefixString;
|
||||
private String plainTextPrefix;
|
||||
private String languageName;
|
||||
private boolean enablePingMessages;
|
||||
private boolean customPlayerCountMessage;
|
||||
@@ -72,6 +78,13 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
private boolean debug;
|
||||
private long savedEndtimer;
|
||||
|
||||
// Webhook settings
|
||||
private boolean webhookEnabled;
|
||||
private Set<String> webhookEnabledEvents;
|
||||
private String webhookUrl;
|
||||
private String webhookUsername;
|
||||
private String webhookAvatarUrl;
|
||||
|
||||
protected Config config;
|
||||
protected Config language;
|
||||
protected Config whitelist;
|
||||
@@ -119,6 +132,7 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
}
|
||||
|
||||
prefixString = language.getString("prefix");
|
||||
plainTextPrefix = PlainTextComponentSerializer.plainText().serialize(parse(prefixString));
|
||||
|
||||
plugin.getEventManager().callEvent(new MaintenanceReloadedEvent());
|
||||
}
|
||||
@@ -161,6 +175,9 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
if (file.exists()) return;
|
||||
|
||||
try (final InputStream in = plugin.getResource(fileName)) {
|
||||
if (in == null) {
|
||||
throw new IOException("No bundled language file " + fileName);
|
||||
}
|
||||
Files.copy(in, file.toPath());
|
||||
} catch (final IOException e) {
|
||||
plugin.getLogger().warning("Unable to provide language " + languageName);
|
||||
@@ -179,14 +196,16 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
final ConfigSection pingMessageSection = config.getSection("ping-message");
|
||||
enablePingMessages = pingMessageSection.getBoolean("enabled", true);
|
||||
// Can't store this pre-parsed as components because gradients will break replacements
|
||||
pingMessages = loadPingMessages(pingMessageSection.getStringList("messages"));
|
||||
pingMessages = loadPingMessages(pingMessageSection.getSection("messages"));
|
||||
if (pingMessageSection.getBoolean("enable-timer-specific-messages")) {
|
||||
timerSpecificPingMessages = loadPingMessages(pingMessageSection.getStringList("timer-messages"));
|
||||
timerSpecificPingMessages = loadPingMessages(pingMessageSection.getSection("timer-messages"));
|
||||
} else {
|
||||
timerSpecificPingMessages = null;
|
||||
}
|
||||
|
||||
maintenance = config.getBoolean("maintenance-enabled");
|
||||
activeMode = normalizeMode(config.getString("active-mode"));
|
||||
activeReason = normalizeReason(config.getString("active-reason"));
|
||||
commandsOnMaintenanceEnable = config.getStringList("commands-on-maintenance-enable");
|
||||
commandsOnMaintenanceDisable = config.getStringList("commands-on-maintenance-disable");
|
||||
customMaintenanceIcon = config.getBoolean("custom-maintenance-icon");
|
||||
@@ -197,34 +216,35 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
if (plugin.getServerType() != ServerType.SPONGE) {
|
||||
final ConfigSection playerCountMessageSection = config.getSection("player-count-message");
|
||||
customPlayerCountMessage = playerCountMessageSection.getBoolean("enabled");
|
||||
legacyParsedPlayerCountMessage = toLegacy(parse(getConfigMessage(playerCountMessageSection, "message")));
|
||||
legacyParsedPlayerCountMessages = loadPlayerCountMessages(playerCountMessageSection.getSection("messages"));
|
||||
if (playerCountMessageSection.getBoolean("enable-timer-specific-message")) {
|
||||
legacyParsedTimerPlayerCountMessage = toLegacy(parse(getConfigMessage(playerCountMessageSection, "timer-message")));
|
||||
legacyParsedTimerPlayerCountMessages = loadPlayerCountMessages(playerCountMessageSection.getSection("timer-messages"));
|
||||
} else {
|
||||
legacyParsedTimerPlayerCountMessage = null;
|
||||
legacyParsedTimerPlayerCountMessages = null;
|
||||
}
|
||||
}
|
||||
|
||||
final ConfigSection listHoverMessageSection = config.getSection("player-list-hover-message");
|
||||
customPlayerCountHoverMessage = listHoverMessageSection.getBoolean("enabled");
|
||||
legacyParsedPlayerCountHoverLines = new ArrayList<>();
|
||||
for (final String line : listHoverMessageSection.getString("message").split("<br>")) {
|
||||
legacyParsedPlayerCountHoverLines.add(toLegacy(parse(line)));
|
||||
}
|
||||
legacyParsedPlayerCountHoverLines = loadPlayerCountHoverMessages(listHoverMessageSection.getSection("messages"));
|
||||
if (listHoverMessageSection.getBoolean("enable-timer-specific-message")) {
|
||||
legacyParsedTimerPlayerCountHoverLines = new ArrayList<>();
|
||||
for (final String line : listHoverMessageSection.getString("timer-message").split("<br>")) {
|
||||
legacyParsedTimerPlayerCountHoverLines.add(toLegacy(parse(line)));
|
||||
}
|
||||
legacyParsedTimerPlayerCountHoverLines = loadPlayerCountHoverMessages(listHoverMessageSection.getSection("timer-messages"));
|
||||
} else {
|
||||
legacyParsedTimerPlayerCountHoverLines = null;
|
||||
}
|
||||
|
||||
languageName = config.getString("language").toLowerCase();
|
||||
languageName = config.getString("language").toLowerCase(Locale.ROOT);
|
||||
kickOnlinePlayers = config.getBoolean("kick-online-players", true);
|
||||
updateChecks = config.getBoolean("update-checks", true);
|
||||
debug = config.getBoolean("debug");
|
||||
|
||||
final ConfigSection webhookSection = config.getSection("webhook");
|
||||
webhookEnabled = webhookSection.getBoolean("enabled");
|
||||
webhookEnabledEvents = new HashSet<>(webhookSection.getStringList("enabled-events"));
|
||||
webhookUrl = webhookSection.getString("url", "");
|
||||
webhookUsername = webhookSection.getString("username", "Maintenance");
|
||||
webhookAvatarUrl = webhookSection.getString("avatar-url", "");
|
||||
|
||||
final ConfigSection section = config.getSection("continue-endtimer-after-restart");
|
||||
saveEndtimerOnStop = section.getBoolean("enabled");
|
||||
savedEndtimer = section.getLong("end");
|
||||
@@ -233,6 +253,12 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
plugin.loadMaintenanceIcon();
|
||||
}
|
||||
|
||||
loadWhitelistedPlayers();
|
||||
|
||||
loadExtraSettings();
|
||||
}
|
||||
|
||||
protected void loadWhitelistedPlayers() {
|
||||
whitelistedPlayers.clear();
|
||||
for (final Map.Entry<String, Object> entry : whitelist.getValues().entrySet()) {
|
||||
try {
|
||||
@@ -241,8 +267,6 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
plugin.getLogger().warning("Invalid WhitelistedPlayers entry: " + entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
loadExtraSettings();
|
||||
}
|
||||
|
||||
private void updatePluginDirectory() {
|
||||
@@ -250,7 +274,7 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
String oldDirName = "Maintenance" + plugin.getServerType();
|
||||
if (plugin.getServerType() == ServerType.SPONGE || plugin.getServerType() == ServerType.VELOCITY) {
|
||||
oldDirName = oldDirName.toLowerCase(Locale.ROOT);
|
||||
} else if (plugin.getServerType() == ServerType.SPIGOT) {
|
||||
} else if (plugin.getServerType() == ServerType.PAPER) {
|
||||
oldDirName = "MaintenanceSpigot";
|
||||
}
|
||||
|
||||
@@ -290,6 +314,35 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
config.move("enable-playercounthovermessage", "player-list-hover-message.enabled");
|
||||
config.move("playercounthovermessage", "player-list-hover-message.message");
|
||||
}
|
||||
if (version < 11) {
|
||||
config.move("player-count-message.message", "player-count-message.messages.default");
|
||||
config.move("player-count-message.timer-message", "player-count-message.timer-messages.default");
|
||||
|
||||
config.move("player-list-hover-message.message", "player-list-hover-message.messages.default");
|
||||
config.move("player-list-hover-message.timer-message", "player-list-hover-message.timer-messages.default");
|
||||
|
||||
migrateModeSection("ping-message.messages");
|
||||
migrateModeSection("ping-message.timer-messages");
|
||||
migrateModeSection("player-count-message.messages");
|
||||
migrateModeSection("player-count-message.timer-messages");
|
||||
migrateModeSection("player-list-hover-message.messages");
|
||||
migrateModeSection("player-list-hover-message.timer-messages");
|
||||
|
||||
final Object proxiedMaintenanceServers = config.getObject("proxied-maintenance-servers");
|
||||
if (proxiedMaintenanceServers instanceof List<?> list) {
|
||||
final Map<String, String> migratedServers = new LinkedHashMap<>();
|
||||
for (final Object entry : list) {
|
||||
if (entry != null) {
|
||||
migratedServers.put(String.valueOf(entry), "default");
|
||||
}
|
||||
}
|
||||
config.set("proxied-maintenance-servers", migratedServers);
|
||||
}
|
||||
}
|
||||
if (version < 12) {
|
||||
// Fix typo in the shipped config key :>
|
||||
config.move("redis.enables", "redis.enabled");
|
||||
}
|
||||
|
||||
createFile("config-new.yml", "config.yml");
|
||||
final File file = new File(plugin.getDataFolder(), "config-new.yml");
|
||||
@@ -321,11 +374,10 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
plugin.getLogger().info("Updating language file to the latest version...");
|
||||
if (version < 1) {
|
||||
for (final Map.Entry<String, Object> entry : language.getValues().entrySet()) {
|
||||
if (!(entry.getValue() instanceof String)) {
|
||||
if (!(entry.getValue() instanceof String value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String value = (String) entry.getValue();
|
||||
value = value.replace("&8[&eMaintenance&8] ", "<prefix>");
|
||||
value = legacyToMinimessage(value);
|
||||
value = value.replace("%NEWLINE%", NEW_LINE_REPLACEMENT);
|
||||
@@ -361,6 +413,17 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
return serialized.replaceAll("</[a-z_]+>", "");
|
||||
}
|
||||
|
||||
private void migrateModeSection(final String path) {
|
||||
final Object section = config.getObject(path);
|
||||
if (section == null || section instanceof Map<?, ?>) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Map<String, Object> map = new LinkedHashMap<>();
|
||||
map.put("default", section);
|
||||
config.set(path, map);
|
||||
}
|
||||
|
||||
private String getConfigMessage(final ConfigSection section, final String path) {
|
||||
final String s = section.getString(path);
|
||||
if (s == null) {
|
||||
@@ -393,6 +456,25 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
return replaceNewlineVar(s).replace("<prefix>", prefixString);
|
||||
}
|
||||
|
||||
public String getWebhookMessage(final String path, final String... replacements) {
|
||||
String s = language.getString(path);
|
||||
if (s == null) {
|
||||
plugin.getLogger().warning("The language file is missing the following webhook message: " + path);
|
||||
return path;
|
||||
}
|
||||
if (replacements.length != 0) {
|
||||
if (replacements.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("Invalid replacement count: " + replacements.length);
|
||||
}
|
||||
|
||||
for (int i = 0; i < replacements.length; i += 2) {
|
||||
final String key = replacements[i];
|
||||
s = s.replace(key, replacements[i + 1]);
|
||||
}
|
||||
}
|
||||
return replaceNewlineVar(s);
|
||||
}
|
||||
|
||||
public @Nullable String getLanguageStringOrNull(final String path, final String... replacements) {
|
||||
return language.contains(path) ? getLanguageString(path, replacements) : null;
|
||||
}
|
||||
@@ -403,24 +485,53 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
|
||||
// TODO Cache platform components if there are no replacements
|
||||
public Component getRandomPingMessage() {
|
||||
if (plugin.isTaskRunning() && !plugin.getRunnable().shouldEnable()
|
||||
&& hasTimerSpecificPingMessages() && !timerSpecificPingMessages.isEmpty()) {
|
||||
return getPingMessage(timerSpecificPingMessages);
|
||||
}
|
||||
return pingMessages.isEmpty() ? Component.empty() : getPingMessage(pingMessages);
|
||||
return getRandomPingMessage(activeMode);
|
||||
}
|
||||
|
||||
private Component getPingMessage(final List<String> list) {
|
||||
final String component = list.size() == 1 ? list.get(0) : list.get(RANDOM.nextInt(list.size()));
|
||||
return parse(plugin.replacePingVariables(component));
|
||||
public Component getRandomPingMessage(@Nullable final String mode) {
|
||||
if (plugin.isTaskRunning() && !plugin.getRunnable().shouldEnable() && hasTimerSpecificPingMessages()) {
|
||||
return getPingMessage(timerSpecificPingMessages, mode);
|
||||
}
|
||||
return pingMessages.isEmpty() ? Component.empty() : getPingMessage(pingMessages, mode);
|
||||
}
|
||||
|
||||
private List<String> loadPingMessages(final List<String> list) {
|
||||
final List<String> components = new ArrayList<>(list.size());
|
||||
for (final String s : list) {
|
||||
components.add(replaceNewlineVar(s));
|
||||
private Component getPingMessage(final DisplayedMessages list, @Nullable final String mode) {
|
||||
final String component = list.getRandomEntry(mode);
|
||||
return parse(plugin.replacePingVariables(component, mode));
|
||||
}
|
||||
|
||||
private DisplayedMessages loadPingMessages(final ConfigSection section) {
|
||||
return loadDisplayedMessages(section, (list, s) -> list.add(replaceNewlineVar(s)));
|
||||
}
|
||||
|
||||
private DisplayedMessages loadPlayerCountHoverMessages(final ConfigSection section) {
|
||||
return loadDisplayedMessages(section, (list, s) -> {
|
||||
// Split into separate messages (one per player entry)
|
||||
for (final String line : s.split("<br>")) {
|
||||
list.add(toLegacy(parse(line)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private DisplayedMessages loadPlayerCountMessages(final ConfigSection section) {
|
||||
return loadDisplayedMessages(section, (list, s) -> list.add(toLegacy(parse(s))));
|
||||
}
|
||||
|
||||
private DisplayedMessages loadDisplayedMessages(final ConfigSection section, final BiConsumer<List<String>, String> messageFunction) {
|
||||
final Map<String, List<String>> modes = new HashMap<>(section.getKeys().size());
|
||||
for (final Map.Entry<String, Object> entry : section.getValues().entrySet()) {
|
||||
final List<String> components = new ArrayList<>();
|
||||
if (entry.getValue() instanceof String s) {
|
||||
messageFunction.accept(components, s);
|
||||
} else if (entry.getValue() instanceof List<?> list) {
|
||||
//noinspection unchecked
|
||||
for (final String s : ((Iterable<String>) list)) {
|
||||
messageFunction.accept(components, s);
|
||||
}
|
||||
}
|
||||
modes.put(entry.getKey(), components);
|
||||
}
|
||||
return components;
|
||||
return new DisplayedMessages(modes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -448,18 +559,19 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
return false;
|
||||
}
|
||||
|
||||
whitelistedPlayers.remove(uuid);
|
||||
whitelist.remove(uuid.toString());
|
||||
saveWhitelistedPlayers();
|
||||
return true;
|
||||
return removeWhitelistedPlayer(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addWhitelistedPlayer(final UUID uuid, final String name) {
|
||||
final boolean added = whitelistedPlayers.put(uuid, name) == null;
|
||||
final boolean added = !name.equals(whitelistedPlayers.put(uuid, name));
|
||||
if (!added) {
|
||||
return false;
|
||||
}
|
||||
|
||||
whitelist.set(uuid.toString(), name);
|
||||
saveWhitelistedPlayers();
|
||||
return added;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -477,8 +589,47 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
return maintenance;
|
||||
}
|
||||
|
||||
public void setMaintenance(final boolean maintenance) {
|
||||
this.maintenance = maintenance;
|
||||
@Override
|
||||
public @Nullable String activeMode() {
|
||||
return activeMode;
|
||||
}
|
||||
|
||||
public void setActiveMode(@Nullable final String activeMode) {
|
||||
final String normalized = normalizeMode(activeMode);
|
||||
if (Objects.equals(this.activeMode, normalized)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeMode = normalized;
|
||||
config.set("active-mode", normalized != null ? normalized : "default");
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String activeReason() {
|
||||
return activeReason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActiveReason(@Nullable final String reason) {
|
||||
final String normalized = normalizeReason(reason);
|
||||
if (Objects.equals(this.activeReason, normalized)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.activeReason = normalized;
|
||||
config.set("active-reason", normalized);
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
public void setMaintenance(final boolean maintenance, @Nullable final String mode) {
|
||||
if (this.maintenance != maintenance) {
|
||||
this.maintenance = maintenance;
|
||||
config.set("maintenance-enabled", maintenance);
|
||||
this.activeMode = normalizeMode(mode);
|
||||
config.set("active-mode", this.activeMode != null ? this.activeMode : "default");
|
||||
saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -535,7 +686,7 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
return config;
|
||||
}
|
||||
|
||||
public List<String> getPingMessages() {
|
||||
public DisplayedMessages getPingMessages() {
|
||||
return pingMessages;
|
||||
}
|
||||
|
||||
@@ -547,7 +698,7 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
return commandsOnMaintenanceDisable;
|
||||
}
|
||||
|
||||
public @Nullable List<String> getTimerSpecificPingMessages() {
|
||||
public @Nullable DisplayedMessages getTimerSpecificPingMessages() {
|
||||
return timerSpecificPingMessages;
|
||||
}
|
||||
|
||||
@@ -555,16 +706,50 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
return broadcastIntervals;
|
||||
}
|
||||
|
||||
public boolean isWebhookEnabled() {
|
||||
return webhookEnabled;
|
||||
}
|
||||
|
||||
public boolean isWebhookEventEnabled(final String event) {
|
||||
return webhookEnabledEvents.contains(event);
|
||||
}
|
||||
|
||||
public String getWebhookUrl() {
|
||||
return webhookUrl;
|
||||
}
|
||||
|
||||
public String getWebhookUsername() {
|
||||
return webhookUsername;
|
||||
}
|
||||
|
||||
public String getWebhookAvatarUrl() {
|
||||
return webhookAvatarUrl;
|
||||
}
|
||||
|
||||
public String getPlainTextPrefix() {
|
||||
return plainTextPrefix;
|
||||
}
|
||||
|
||||
public String getLegacyParsedPlayerCountMessage() {
|
||||
final String message = timerDependent(legacyParsedPlayerCountMessage, legacyParsedTimerPlayerCountMessage);
|
||||
return plugin.replacePingVariables(message);
|
||||
return getLegacyParsedPlayerCountMessage(activeMode);
|
||||
}
|
||||
|
||||
public String getLegacyParsedPlayerCountMessage(@Nullable final String mode) {
|
||||
final DisplayedMessages messages = timerDependent(legacyParsedPlayerCountMessages, legacyParsedTimerPlayerCountMessages);
|
||||
final String message = messages.getRandomEntry(mode);
|
||||
return plugin.replacePingVariables(message, mode);
|
||||
}
|
||||
|
||||
public String[] getLegacyParsedPlayerCountHoverLines() {
|
||||
final List<String> lines = timerDependent(legacyParsedPlayerCountHoverLines, legacyParsedTimerPlayerCountHoverLines);
|
||||
return getLegacyParsedPlayerCountHoverLines(activeMode);
|
||||
}
|
||||
|
||||
public String[] getLegacyParsedPlayerCountHoverLines(@Nullable final String mode) {
|
||||
final DisplayedMessages messages = timerDependent(legacyParsedPlayerCountHoverLines, legacyParsedTimerPlayerCountHoverLines);
|
||||
final List<String> lines = messages.getMessages(mode);
|
||||
final String[] parsedLines = new String[lines.size()];
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
parsedLines[i] = plugin.replacePingVariables(lines.get(i));
|
||||
parsedLines[i] = plugin.replacePingVariables(lines.get(i), mode);
|
||||
}
|
||||
return parsedLines;
|
||||
}
|
||||
@@ -598,7 +783,7 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
}
|
||||
|
||||
/*
|
||||
* Note on why this even exists: Yaml will force save all strings containing line breaks '\n' in this rather chunky format:
|
||||
* Note on why this even exists: YAML will force save all strings containing line breaks '\n' in this rather chunky format:
|
||||
* key: |-
|
||||
* First line text
|
||||
* Second line text.
|
||||
@@ -611,6 +796,17 @@ public class Settings implements eu.kennytv.maintenance.api.Settings {
|
||||
return s.replace(NEW_LINE_REPLACEMENT, "\n");
|
||||
}
|
||||
|
||||
protected @Nullable String normalizeMode(@Nullable final String mode) {
|
||||
if (mode == null || mode.isBlank() || mode.equalsIgnoreCase("default")) {
|
||||
return null;
|
||||
}
|
||||
return mode.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
private String normalizeReason(@Nullable final String reason) {
|
||||
return reason == null || reason.isBlank() ? "" : reason;
|
||||
}
|
||||
|
||||
protected void loadExtraSettings() {
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ package eu.kennytv.maintenance.core.command;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.Settings;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
@@ -28,8 +28,10 @@ import eu.kennytv.maintenance.core.command.subcommand.MotdCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.ReloadCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.RemoveMotdCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.ScheduleTimerCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.SetReasonCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.SetMotdCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.StarttimerCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.StatusCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.ToggleCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.UpdateCommand;
|
||||
import eu.kennytv.maintenance.core.command.subcommand.WhitelistAddCommand;
|
||||
@@ -74,6 +76,7 @@ public abstract class MaintenanceCommand {
|
||||
add(new SetMotdCommand(plugin), "setmotd");
|
||||
add(new RemoveMotdCommand(plugin), "removemotd");
|
||||
add(new MotdCommand(plugin), "motd");
|
||||
add(new SetReasonCommand(plugin), "setreason");
|
||||
|
||||
add(new UpdateCommand(plugin), "update", "forceupdate");
|
||||
add(new DumpCommand(plugin), "dump");
|
||||
@@ -97,7 +100,7 @@ public abstract class MaintenanceCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
final CommandInfo command = commandExecutors.get(args[0].toLowerCase());
|
||||
final CommandInfo command = commandExecutors.get(args[0].toLowerCase(Locale.ROOT));
|
||||
if (command == null) {
|
||||
help.sendUsage(sender);
|
||||
return;
|
||||
@@ -112,7 +115,7 @@ public abstract class MaintenanceCommand {
|
||||
|
||||
public List<String> getSuggestions(final SenderInfo sender, final String[] args) {
|
||||
if (!sender.hasMaintenancePermission("command") || args.length == 0) return Collections.emptyList();
|
||||
final String s = args[0].toLowerCase();
|
||||
final String s = args[0].toLowerCase(Locale.ROOT);
|
||||
if (args.length == 1) {
|
||||
final List<String> list = new ArrayList<>();
|
||||
for (final Map.Entry<String, CommandInfo> entry : commandExecutors.entrySet()) {
|
||||
@@ -129,6 +132,24 @@ public abstract class MaintenanceCommand {
|
||||
return info != null && info.hasPermission(sender) ? info.getTabCompletion(sender, args) : Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a user given time like {@code 2d}, {@code 2d12h}, {@code 1h5m}, or {@code 30s} to an ISO-8601 duration string.
|
||||
* Days are supported in addition to hours, minutes, and seconds.
|
||||
*
|
||||
* @return ISO duration string
|
||||
*/
|
||||
private static String toIsoDuration(final String time) {
|
||||
final String upper = time.toUpperCase(Locale.ROOT);
|
||||
final int dayIndex = upper.indexOf('D');
|
||||
if (dayIndex == -1) {
|
||||
return "PT" + upper;
|
||||
}
|
||||
|
||||
final String days = upper.substring(0, dayIndex + 1);
|
||||
final String rest = upper.substring(dayIndex + 1);
|
||||
return rest.isEmpty() ? "P" + days : "P" + days + "T" + rest;
|
||||
}
|
||||
|
||||
public @Nullable Duration parseDurationAndCheckTask(final SenderInfo sender, final String time, final boolean taskCheck) {
|
||||
final Duration duration;
|
||||
if (plugin.isNumeric(time)) {
|
||||
@@ -136,8 +157,7 @@ public abstract class MaintenanceCommand {
|
||||
duration = Duration.ofMinutes(Integer.parseInt(time));
|
||||
} else {
|
||||
try {
|
||||
// Only accept hours, minutes, and seconds
|
||||
duration = Duration.parse("PT" + time.toUpperCase(Locale.ROOT));
|
||||
duration = Duration.parse(toIsoDuration(time));
|
||||
} catch (final DateTimeParseException e) {
|
||||
return null;
|
||||
}
|
||||
@@ -168,6 +188,7 @@ public abstract class MaintenanceCommand {
|
||||
|
||||
protected void addToggleAndTimerCommands() {
|
||||
add(new ToggleCommand(plugin), "on", "off");
|
||||
add(new StatusCommand(plugin), "status");
|
||||
|
||||
add(new StarttimerCommand(plugin), "starttimer", "start");
|
||||
add(new EndtimerCommand(plugin), "endtimer", "end");
|
||||
|
||||
@@ -20,12 +20,14 @@ package eu.kennytv.maintenance.core.command.subcommand;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.TextComponent;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.event.ClickEvent;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.event.HoverEvent;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.format.NamedTextColor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
|
||||
import static eu.kennytv.maintenance.core.util.ComponentUtil.clickEvent;
|
||||
|
||||
public final class DumpCommand extends CommandInfo {
|
||||
private long lastDump;
|
||||
@@ -57,7 +59,7 @@ public final class DumpCommand extends CommandInfo {
|
||||
sender.sendPrefixedRich("<red><click:open_url:'" + url + "'>" + url + "</click>");
|
||||
if (sender.isPlayer()) {
|
||||
final TextComponent text = Component.text().content("Click here to copy the link").color(NamedTextColor.GRAY)
|
||||
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, url))
|
||||
.clickEvent(clickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, url))
|
||||
.hoverEvent(HoverEvent.showText(Component.text("Click here to copy the link to your clipboard").color(NamedTextColor.GREEN)))
|
||||
.build();
|
||||
sender.send(plugin.prefix().append(text));
|
||||
|
||||
+8
-2
@@ -19,9 +19,10 @@ package eu.kennytv.maintenance.core.command.subcommand;
|
||||
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.util.DiscordWebhook;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import java.time.Duration;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public final class EndtimerCommand extends CommandInfo {
|
||||
|
||||
@@ -44,6 +45,11 @@ public final class EndtimerCommand extends CommandInfo {
|
||||
}
|
||||
|
||||
plugin.startMaintenanceRunnable(duration, false);
|
||||
sender.send(getMessage("endtimerStarted", "%TIME%", plugin.getRunnable().getTime()));
|
||||
|
||||
final Component message = getMessage("endtimerStarted", "%TIME%", plugin.getRunnable().getTime());
|
||||
sender.send(message);
|
||||
plugin.sendWebhookMessage("webhookEndtimerStarted", DiscordWebhook.EventType.ENDTIMER_STARTED,
|
||||
"%TIME%", plugin.getRunnable().getTime(),
|
||||
"%TIMESTAMP%", plugin.getTargetTimestamp(duration));
|
||||
}
|
||||
}
|
||||
@@ -21,9 +21,9 @@ import com.google.common.base.Preconditions;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public final class HelpCommand extends CommandInfo {
|
||||
private static final int COMMANDS_PER_PAGE = 8;
|
||||
|
||||
+49
-10
@@ -19,10 +19,14 @@ package eu.kennytv.maintenance.core.command.subcommand;
|
||||
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.config.ConfigSection;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class MotdCommand extends CommandInfo {
|
||||
|
||||
@@ -32,21 +36,29 @@ public final class MotdCommand extends CommandInfo {
|
||||
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (args.length == 1) {
|
||||
sendList(sender, getSettings().getPingMessages());
|
||||
} else if (args.length == 2 && args[1].equalsIgnoreCase("timer")) {
|
||||
int indexOffset = 1;
|
||||
boolean timer = false;
|
||||
if (args.length > indexOffset && args[indexOffset].equalsIgnoreCase("timer")) {
|
||||
if (!getSettings().hasTimerSpecificPingMessages()) {
|
||||
sender.send(getMessage("timerMotdDisabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
sendList(sender, getSettings().getTimerSpecificPingMessages());
|
||||
} else {
|
||||
sender.send(getHelpMessage());
|
||||
timer = true;
|
||||
indexOffset++;
|
||||
}
|
||||
|
||||
if (args.length > indexOffset + 1) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final String mode = args.length == indexOffset + 1 ? args[indexOffset].toLowerCase(Locale.ROOT) : "default";
|
||||
final ConfigSection section = getSettings().getConfig().getSection("ping-message").getSection(timer ? "timer-messages" : "messages");
|
||||
sendList(sender, getModeMessages(section, mode));
|
||||
}
|
||||
|
||||
private void sendList(final SenderInfo sender, final List<String> list) {
|
||||
private void sendList(final SenderInfo sender, @Nullable final List<String> list) {
|
||||
if (list == null || list.isEmpty()) {
|
||||
sender.send(getMessage("motdListEmpty"));
|
||||
return;
|
||||
@@ -55,16 +67,43 @@ public final class MotdCommand extends CommandInfo {
|
||||
sender.send(getMessage("motdList"));
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
sender.sendRich("<aqua>" + (i + 1) + "<dark_gray><st>---------");
|
||||
sender.sendRich(list.get(i));
|
||||
sender.sendRich(list.get(i).replace("<br>", "\n"));
|
||||
}
|
||||
sender.sendRich("<dark_gray><st>----------");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabCompletion(final SenderInfo sender, final String[] args) {
|
||||
if (args.length != 2 || !getSettings().hasTimerSpecificPingMessages()) {
|
||||
if (args.length < 2 || args.length > 3) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.singletonList("timer");
|
||||
|
||||
if (args.length == 2) {
|
||||
final List<String> list = new ArrayList<>();
|
||||
if (getSettings().hasTimerSpecificPingMessages()) {
|
||||
list.add("timer");
|
||||
}
|
||||
|
||||
final List<String> modes = new ArrayList<>(getSettings().getConfig().getSection("ping-message").getSection("messages").getKeys());
|
||||
modes.remove("default");
|
||||
list.addAll(modes);
|
||||
return list;
|
||||
}
|
||||
|
||||
if (!args[1].equalsIgnoreCase("timer") || !getSettings().hasTimerSpecificPingMessages()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final List<String> modes = new ArrayList<>(getSettings().getConfig().getSection("ping-message").getSection("timer-messages").getKeys());
|
||||
modes.remove("default");
|
||||
return modes;
|
||||
}
|
||||
|
||||
private @Nullable List<String> getModeMessages(final ConfigSection section, final String mode) {
|
||||
final Object value = section.getObject(mode);
|
||||
if (value instanceof String s) {
|
||||
return Collections.singletonList(s);
|
||||
}
|
||||
return section.getStringList(mode);
|
||||
}
|
||||
}
|
||||
+68
-15
@@ -22,10 +22,10 @@ import eu.kennytv.maintenance.core.Settings;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.config.ConfigSection;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class RemoveMotdCommand extends CommandInfo {
|
||||
|
||||
@@ -34,32 +34,49 @@ public final class RemoveMotdCommand extends CommandInfo {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, String[] args) {
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
int indexOffset = 1;
|
||||
boolean timerPingMessages = false;
|
||||
if (args.length == 3 && args[1].equalsIgnoreCase("timer")) {
|
||||
if (args.length > indexOffset && args[indexOffset].equalsIgnoreCase("timer")) {
|
||||
if (!getSettings().hasTimerSpecificPingMessages()) {
|
||||
sender.send(getMessage("timerMotdDisabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
args = plugin.removeArrayIndex(args, 1);
|
||||
indexOffset++;
|
||||
timerPingMessages = true;
|
||||
}
|
||||
if (checkArgs(sender, args, 2)) return;
|
||||
if (!plugin.isNumeric(args[1])) {
|
||||
|
||||
if (args.length <= indexOffset) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
String mode = "default";
|
||||
if (!plugin.isNumeric(args[indexOffset])) {
|
||||
mode = args[indexOffset].toLowerCase(Locale.ROOT);
|
||||
indexOffset++;
|
||||
}
|
||||
|
||||
if (args.length != indexOffset + 1 || !plugin.isNumeric(args[indexOffset])) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final Settings settings = getSettings();
|
||||
final ConfigSection section = settings.getConfig().getSection("ping-message");
|
||||
final List<String> pingMessages = section.getStringList(timerPingMessages ? "timer-messages" : "messages");
|
||||
if (pingMessages.size() < 2) {
|
||||
final ConfigSection messageSection = section.getSection(timerPingMessages ? "timer-messages" : "messages");
|
||||
final List<String> pingMessages = getModeMessages(messageSection, mode);
|
||||
if (pingMessages.isEmpty()) {
|
||||
sender.send(getMessage("motdListEmpty"));
|
||||
return;
|
||||
}
|
||||
if (mode.equals("default") && pingMessages.size() < 2) {
|
||||
sender.send(getMessage("removeMotdError"));
|
||||
return;
|
||||
}
|
||||
|
||||
final int index = Integer.parseInt(args[1]);
|
||||
final int index = Integer.parseInt(args[indexOffset]);
|
||||
if (index == 0 || index > pingMessages.size()) {
|
||||
sender.send(getMessage("setMotdIndexError",
|
||||
"%MOTDS%", Integer.toString(pingMessages.size()),
|
||||
@@ -67,24 +84,60 @@ public final class RemoveMotdCommand extends CommandInfo {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<String> pingComponents = timerPingMessages ? settings.getTimerSpecificPingMessages() : settings.getPingMessages();
|
||||
pingComponents.remove(index - 1);
|
||||
pingMessages.remove(index - 1);
|
||||
section.set(timerPingMessages ? "timer-messages" : "messages", pingMessages);
|
||||
if (pingMessages.isEmpty()) {
|
||||
messageSection.remove(mode);
|
||||
} else {
|
||||
messageSection.set(mode, pingMessages);
|
||||
}
|
||||
settings.saveConfig();
|
||||
sender.send(getMessage("removedMotd", "%INDEX%", args[1]));
|
||||
settings.reloadConfigs();
|
||||
sender.send(getMessage("removedMotd", "%INDEX%", Integer.toString(index)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabCompletion(final SenderInfo sender, final String[] args) {
|
||||
if (args.length == 2 || (args.length == 3 && args[1].equalsIgnoreCase("timer"))) {
|
||||
final int size = (args.length == 3 ? plugin.getSettings().getTimerSpecificPingMessages() : plugin.getSettings().getPingMessages()).size();
|
||||
final int indexOffset;
|
||||
final boolean timer;
|
||||
if (args.length > 1 && args[1].equalsIgnoreCase("timer")) {
|
||||
timer = true;
|
||||
indexOffset = 2;
|
||||
} else {
|
||||
timer = false;
|
||||
indexOffset = 1;
|
||||
}
|
||||
|
||||
if (args.length == indexOffset + 1 && !plugin.isNumeric(args[indexOffset])) {
|
||||
return getModeCompletions(timer);
|
||||
}
|
||||
|
||||
if (args.length == indexOffset + 1 || (args.length == indexOffset + 2 && !plugin.isNumeric(args[indexOffset]))) {
|
||||
final String mode = plugin.isNumeric(args[indexOffset]) ? "default" : args[indexOffset].toLowerCase(Locale.ROOT);
|
||||
final int size = getModeMessages(getSettings().getConfig().getSection("ping-message").getSection(timer ? "timer-messages" : "messages"), mode).size();
|
||||
final List<String> list = new ArrayList<>(size);
|
||||
for (int i = 1; i <= size; i++) {
|
||||
list.add(Integer.toString(i));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private List<String> getModeCompletions(final boolean timer) {
|
||||
final List<String> modes = new ArrayList<>(getSettings().getConfig().getSection("ping-message").getSection(timer ? "timer-messages" : "messages").getKeys());
|
||||
modes.remove("default");
|
||||
return modes;
|
||||
}
|
||||
|
||||
private List<String> getModeMessages(final ConfigSection section, final String mode) {
|
||||
final Object value = section.getObject(mode);
|
||||
if (value == null) {
|
||||
return List.of();
|
||||
}
|
||||
if (value instanceof String s) {
|
||||
return new ArrayList<>(Collections.singletonList(s));
|
||||
}
|
||||
return new ArrayList<>(section.getStringList(mode));
|
||||
}
|
||||
}
|
||||
+76
-29
@@ -26,6 +26,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static eu.kennytv.maintenance.core.Settings.NEW_LINE_REPLACEMENT;
|
||||
|
||||
@@ -36,28 +37,41 @@ public final class SetMotdCommand extends CommandInfo {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, String[] args) {
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
int indexOffset = 1;
|
||||
boolean timerPingMessages = false;
|
||||
if (args.length > 1 && args[1].equalsIgnoreCase("timer")) {
|
||||
if (args.length > indexOffset && args[indexOffset].equalsIgnoreCase("timer")) {
|
||||
if (!getSettings().hasTimerSpecificPingMessages()) {
|
||||
sender.send(getMessage("timerMotdDisabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
// remove the "timer" off the args to keep the rest the code cleaner
|
||||
args = plugin.removeArrayIndex(args, 1);
|
||||
indexOffset++;
|
||||
timerPingMessages = true;
|
||||
}
|
||||
if (args.length < 4 || !plugin.isNumeric(args[1])) {
|
||||
|
||||
if (args.length <= indexOffset + 2) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
String mode = "default";
|
||||
if (!plugin.isNumeric(args[indexOffset])) {
|
||||
mode = args[indexOffset].toLowerCase(Locale.ROOT);
|
||||
indexOffset++;
|
||||
}
|
||||
|
||||
if (args.length <= indexOffset + 2 || !plugin.isNumeric(args[indexOffset])) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final Settings settings = getSettings();
|
||||
final List<String> pingComponents = timerPingMessages ? settings.getTimerSpecificPingMessages() : settings.getPingMessages();
|
||||
final ConfigSection section = settings.getConfig().getSection("ping-message");
|
||||
final List<String> pingMessages = section.getStringList(timerPingMessages ? "timer-messages" : "messages");
|
||||
final int index = Integer.parseInt(args[1]);
|
||||
final ConfigSection messageSection = section.getSection(timerPingMessages ? "timer-messages" : "messages");
|
||||
final List<String> pingMessages = getOrCreateModeMessages(messageSection, mode);
|
||||
|
||||
final int index = Integer.parseInt(args[indexOffset]);
|
||||
if (index == 0 || index > pingMessages.size() + 1) {
|
||||
sender.send(getMessage("setMotdIndexError",
|
||||
"%MOTDS%", Integer.toString(pingMessages.size()),
|
||||
@@ -65,63 +79,96 @@ public final class SetMotdCommand extends CommandInfo {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!plugin.isNumeric(args[2])) {
|
||||
if (!plugin.isNumeric(args[indexOffset + 1])) {
|
||||
sender.send(getMessage("setMotdLineError"));
|
||||
return;
|
||||
}
|
||||
|
||||
final int line = Integer.parseInt(args[2]);
|
||||
final int line = Integer.parseInt(args[indexOffset + 1]);
|
||||
if (line != 1 && line != 2) {
|
||||
sender.send(getMessage("setMotdLineError"));
|
||||
return;
|
||||
}
|
||||
|
||||
final String message = String.join(" ", Arrays.copyOfRange(args, 3, args.length));
|
||||
final String message = String.join(" ", Arrays.copyOfRange(args, indexOffset + 2, args.length));
|
||||
final String oldMessage = index > pingMessages.size() ? "" : pingMessages.get(index - 1);
|
||||
final String newMessage;
|
||||
if (line == 1) {
|
||||
newMessage = oldMessage.contains(NEW_LINE_REPLACEMENT) ?
|
||||
message + NEW_LINE_REPLACEMENT + oldMessage.split(NEW_LINE_REPLACEMENT, 2)[1] : message;
|
||||
newMessage = oldMessage.contains(NEW_LINE_REPLACEMENT)
|
||||
? message + NEW_LINE_REPLACEMENT + oldMessage.split(NEW_LINE_REPLACEMENT, 2)[1]
|
||||
: message;
|
||||
} else {
|
||||
newMessage = oldMessage.contains(NEW_LINE_REPLACEMENT) ?
|
||||
oldMessage.split(NEW_LINE_REPLACEMENT, 2)[0] + NEW_LINE_REPLACEMENT + message : oldMessage + NEW_LINE_REPLACEMENT + message;
|
||||
newMessage = oldMessage.contains(NEW_LINE_REPLACEMENT)
|
||||
? oldMessage.split(NEW_LINE_REPLACEMENT, 2)[0] + NEW_LINE_REPLACEMENT + message
|
||||
: oldMessage + NEW_LINE_REPLACEMENT + message;
|
||||
}
|
||||
|
||||
// Replace component in config and the cached list
|
||||
final String componentString = newMessage.replace(NEW_LINE_REPLACEMENT, "\n");
|
||||
if (index > pingMessages.size()) {
|
||||
pingMessages.add(newMessage);
|
||||
pingComponents.add(componentString);
|
||||
} else {
|
||||
pingMessages.set(index - 1, newMessage);
|
||||
pingComponents.set(index - 1, componentString);
|
||||
}
|
||||
|
||||
section.set(timerPingMessages ? "timer-messages" : "messages", pingMessages);
|
||||
messageSection.set(mode, pingMessages);
|
||||
settings.saveConfig();
|
||||
settings.reloadConfigs();
|
||||
|
||||
sender.send(settings.getMessage(
|
||||
"setMotd",
|
||||
"%LINE%", args[2],
|
||||
"%INDEX%", args[1],
|
||||
"%MOTD%", componentString
|
||||
"%LINE%", Integer.toString(line),
|
||||
"%INDEX%", Integer.toString(index),
|
||||
"%MOTD%", newMessage.replace(NEW_LINE_REPLACEMENT, "\n")
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabCompletion(final SenderInfo sender, String[] args) {
|
||||
boolean timer = false;
|
||||
public List<String> getTabCompletion(final SenderInfo sender, final String[] args) {
|
||||
final int indexOffset;
|
||||
final boolean timer;
|
||||
if (args.length > 1 && args[1].equalsIgnoreCase("timer")) {
|
||||
args = plugin.removeArrayIndex(args, 1);
|
||||
timer = true;
|
||||
indexOffset = 2;
|
||||
} else {
|
||||
timer = false;
|
||||
indexOffset = 1;
|
||||
}
|
||||
if (args.length == 3) return Arrays.asList("1", "2");
|
||||
if (args.length == 2) {
|
||||
|
||||
if (args.length == indexOffset + 2) {
|
||||
return Arrays.asList("1", "2");
|
||||
}
|
||||
|
||||
if (args.length == indexOffset + 1 && !plugin.isNumeric(args[indexOffset])) {
|
||||
return getModeCompletions(timer);
|
||||
}
|
||||
|
||||
if (args.length == indexOffset + 1) {
|
||||
final String mode = plugin.isNumeric(args[indexOffset]) ? "default" : args[indexOffset].toLowerCase(Locale.ROOT);
|
||||
final ConfigSection section = getSettings().getConfig().getSection("ping-message").getSection(timer ? "timer-messages" : "messages");
|
||||
final int size = getOrCreateModeMessages(section, mode).size();
|
||||
final List<String> list = new ArrayList<>();
|
||||
for (int i = 1; i <= (timer ? getSettings().getTimerSpecificPingMessages() : getSettings().getPingMessages()).size() + 1; i++) {
|
||||
for (int i = 1; i <= size + 1; i++) {
|
||||
list.add(String.valueOf(i));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private List<String> getModeCompletions(final boolean timer) {
|
||||
final List<String> modes = new ArrayList<>(getSettings().getConfig().getSection("ping-message").getSection(timer ? "timer-messages" : "messages").getKeys());
|
||||
modes.remove("default");
|
||||
return modes;
|
||||
}
|
||||
|
||||
private List<String> getOrCreateModeMessages(final ConfigSection section, final String mode) {
|
||||
final Object value = section.getObject(mode);
|
||||
if (value == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if (value instanceof String s) {
|
||||
return new ArrayList<>(Collections.singletonList(s));
|
||||
}
|
||||
return new ArrayList<>(section.getStringList(mode));
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.command.subcommand;
|
||||
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public final class SetReasonCommand extends CommandInfo {
|
||||
|
||||
public SetReasonCommand(final MaintenancePlugin plugin) {
|
||||
super(plugin, "setreason");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (args.length < 2) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final String value = String.join(" ", java.util.Arrays.copyOfRange(args, 1, args.length));
|
||||
final String reason;
|
||||
if (value.equalsIgnoreCase("none") || value.equalsIgnoreCase("clear") || value.equals("-")) {
|
||||
reason = null;
|
||||
} else {
|
||||
reason = value;
|
||||
}
|
||||
|
||||
getSettings().setActiveReason(reason);
|
||||
sender.send(getMessage("setReason", "%REASON%", reason == null ? "-" : reason));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabCompletion(final SenderInfo sender, final String[] args) {
|
||||
if (args.length != 2) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.singletonList("none");
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -19,9 +19,10 @@ package eu.kennytv.maintenance.core.command.subcommand;
|
||||
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.util.DiscordWebhook;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import java.time.Duration;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public final class StarttimerCommand extends CommandInfo {
|
||||
|
||||
@@ -44,6 +45,11 @@ public final class StarttimerCommand extends CommandInfo {
|
||||
}
|
||||
|
||||
plugin.startMaintenanceRunnable(duration, true);
|
||||
sender.send(getMessage("starttimerStarted", "%TIME%", plugin.getRunnable().getTime()));
|
||||
|
||||
final Component message = getMessage("starttimerStarted", "%TIME%", plugin.getRunnable().getTime());
|
||||
sender.send(message);
|
||||
plugin.sendWebhookMessage("webhookStarttimerStarted", DiscordWebhook.EventType.STARTTIMER_STARTED,
|
||||
"%TIME%", plugin.getRunnable().getTime(),
|
||||
"%TIMESTAMP%", plugin.getTargetTimestamp(duration));
|
||||
}
|
||||
}
|
||||
+19
-14
@@ -15,28 +15,33 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.proxy.command.subcommand;
|
||||
package eu.kennytv.maintenance.core.command.subcommand;
|
||||
|
||||
import eu.kennytv.maintenance.core.proxy.MaintenanceProxyPlugin;
|
||||
import eu.kennytv.maintenance.core.proxy.command.ProxyCommandInfo;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.runnable.MaintenanceRunnable;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public final class StatusCommand extends ProxyCommandInfo {
|
||||
public final class StatusCommand extends CommandInfo {
|
||||
|
||||
public StatusCommand(final MaintenanceProxyPlugin plugin) {
|
||||
super(plugin, "singleserver.status");
|
||||
public StatusCommand(final MaintenancePlugin plugin) {
|
||||
super(plugin, "status");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (getSettings().getMaintenanceServers().isEmpty()) {
|
||||
sender.send(getMessage("singleServerMaintenanceListEmpty"));
|
||||
return;
|
||||
}
|
||||
|
||||
sender.send(getMessage("singleServerMaintenanceList"));
|
||||
for (final String server : getSettings().getMaintenanceServers()) {
|
||||
sender.send(getMessage("singleServerMaintenanceListEntry", "%SERVER%", server));
|
||||
final MaintenanceRunnable task = plugin.getCurrentTask();
|
||||
if (task != null) {
|
||||
final String key = task.shouldEnable() ? "maintenanceStatusOffWithStartTimer" : "maintenanceStatusOnWithEndTimer";
|
||||
sender.send(getMessage(key, "%TIME%", task.getTime()));
|
||||
} else {
|
||||
sender.send(getMessage(plugin.isMaintenance() ? "maintenanceStatusOn" : "maintenanceStatusOff"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getHelpMessage() {
|
||||
return getMessage("helpGlobalStatus");
|
||||
}
|
||||
}
|
||||
+37
-2
@@ -20,6 +20,10 @@ package eu.kennytv.maintenance.core.command.subcommand;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class ToggleCommand extends CommandInfo {
|
||||
|
||||
@@ -29,14 +33,45 @@ public final class ToggleCommand extends CommandInfo {
|
||||
|
||||
@Override
|
||||
public void execute(final SenderInfo sender, final String[] args) {
|
||||
if (checkArgs(sender, args, 1)) return;
|
||||
if (args.length == 0 || args.length > 2) {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean maintenance = args[0].equalsIgnoreCase("on");
|
||||
final String mode;
|
||||
if (maintenance && args.length == 2) {
|
||||
mode = args[1].toLowerCase(Locale.ROOT);
|
||||
} else if (!maintenance && args.length == 1) {
|
||||
mode = null;
|
||||
} else if (maintenance) {
|
||||
mode = null;
|
||||
} else {
|
||||
sender.send(getHelpMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
if (maintenance == plugin.isMaintenance()) {
|
||||
if (mode != null) {
|
||||
plugin.getSettings().setActiveMode(mode);
|
||||
return;
|
||||
}
|
||||
|
||||
sender.send(getMessage(maintenance ? "alreadyEnabled" : "alreadyDisabled"));
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.setMaintenance(maintenance);
|
||||
plugin.setMaintenance(maintenance, mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabCompletion(final SenderInfo sender, final String[] args) {
|
||||
if (args.length != 2 || !args[0].equalsIgnoreCase("on")) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final List<String> modes = new ArrayList<>(getSettings().getPingMessages().getKeys());
|
||||
modes.remove("default");
|
||||
return modes;
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -20,11 +20,11 @@ package eu.kennytv.maintenance.core.command.subcommand;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.event.ClickEvent;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.event.HoverEvent;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.format.NamedTextColor;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
|
||||
public final class UpdateCommand extends CommandInfo {
|
||||
|
||||
|
||||
+3
-3
@@ -99,10 +99,10 @@ public final class WhitelistAddCommand extends CommandInfo {
|
||||
}
|
||||
|
||||
private void addPlayerToWhitelist(final SenderInfo sender, final SenderInfo selected) {
|
||||
if (getSettings().addWhitelistedPlayer(selected.getUuid(), selected.getName())) {
|
||||
sender.send(getMessage("whitelistAdded", "%PLAYER%", selected.getName()));
|
||||
if (getSettings().addWhitelistedPlayer(selected.uuid(), selected.name())) {
|
||||
sender.send(getMessage("whitelistAdded", "%PLAYER%", selected.name()));
|
||||
} else {
|
||||
sender.send(getMessage("whitelistAlreadyAdded", "%PLAYER%", selected.getName()));
|
||||
sender.send(getMessage("whitelistAlreadyAdded", "%PLAYER%", selected.name()));
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -20,10 +20,10 @@ package eu.kennytv.maintenance.core.command.subcommand;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.command.CommandInfo;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public final class WhitelistCommand extends CommandInfo {
|
||||
|
||||
|
||||
+6
-5
@@ -24,6 +24,7 @@ import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class WhitelistRemoveCommand extends CommandInfo {
|
||||
@@ -50,9 +51,9 @@ public final class WhitelistRemoveCommand extends CommandInfo {
|
||||
if (args.length != 2) return Collections.emptyList();
|
||||
|
||||
final List<String> list = new ArrayList<>();
|
||||
String matchNameLowerCase = args[1].toLowerCase();
|
||||
String matchNameLowerCase = args[1].toLowerCase(Locale.ROOT);
|
||||
for (final String name : getSettings().getWhitelistedPlayers().values()) {
|
||||
if (name.toLowerCase().startsWith(matchNameLowerCase)) {
|
||||
if (name.toLowerCase(Locale.ROOT).startsWith(matchNameLowerCase)) {
|
||||
list.add(name);
|
||||
}
|
||||
}
|
||||
@@ -70,7 +71,7 @@ public final class WhitelistRemoveCommand extends CommandInfo {
|
||||
return;
|
||||
}
|
||||
|
||||
removePlayerFromWhitelist(sender, selected.getUuid(), selected.getName());
|
||||
removePlayerFromWhitelist(sender, selected.uuid(), selected.name());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -79,13 +80,13 @@ public final class WhitelistRemoveCommand extends CommandInfo {
|
||||
if (selected == null) {
|
||||
removePlayerFromWhitelist(sender, uuid, uuid.toString());
|
||||
} else {
|
||||
removePlayerFromWhitelist(sender, selected.getUuid(), selected.getName());
|
||||
removePlayerFromWhitelist(sender, selected.uuid(), selected.name());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void removePlayerFromWhitelist(final SenderInfo sender, final UUID uuid, final String toReplace) {
|
||||
if (getSettings().removeWhitelistedPlayer(uuid)) {
|
||||
if (plugin.getSettings().removeWhitelistedPlayer(uuid)) {
|
||||
sender.send(getMessage("whitelistRemoved", "%PLAYER%", toReplace));
|
||||
} else {
|
||||
sender.send(getMessage("whitelistNotFound"));
|
||||
|
||||
@@ -17,10 +17,8 @@
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.config;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -40,18 +38,20 @@ import org.yaml.snakeyaml.Yaml;
|
||||
public final class Config extends ConfigSection {
|
||||
|
||||
private static final String AWESOME_HEADER =
|
||||
"###################################################################################################################\n" +
|
||||
"# __ __ _ _ _ _ _ #\n" +
|
||||
"# | \\/ | __ _(_)_ __ | |_ ___ _ __ __ _ _ __ ___ ___ | |__ _ _ | | _____ _ __ _ __ _ _| |___ __ #\n" +
|
||||
"# | |\\/| |/ _` | | '_ \\| __/ _ \\ '_ \\ / _` | '_ \\ / __/ _ \\ | '_ \\| | | | | |/ / _ \\ '_ \\| '_ \\| | | | __\\ \\ / / #\n" +
|
||||
"# | | | | (_| | | | | | || __/ | | | (_| | | | | (_| __/ | |_) | |_| | | < __/ | | | | | | |_| | |_ \\ V / #\n" +
|
||||
"# |_| |_|\\__,_|_|_| |_|\\__\\___|_| |_|\\__,_|_| |_|\\___\\___| |_.__/ \\__, | |_|\\_\\___|_| |_|_| |_|\\__, |\\__| \\_/ #\n" +
|
||||
"# |___/ |___/ #\n" +
|
||||
"###################################################################################################################\n" +
|
||||
"# You can report bugs here: https://github.com/kennytv/Maintenance/issues\n" +
|
||||
"# If you need any other help/support, you can also join my Discord server: https://discord.gg/vGCUzHq\n" +
|
||||
"# The config and language files use MiniMessage, NOT legacy text for input. Use https://webui.adventure.kyori.net/ to edit and preview the formatted text.\n" +
|
||||
"# For a full list of formats and fancy examples of MiniMessage, see https://docs.adventure.kyori.net/minimessage/format.html\n";
|
||||
"""
|
||||
###################################################################################################################
|
||||
# __ __ _ _ _ _ _ #
|
||||
# | \\/ | __ _(_)_ __ | |_ ___ _ __ __ _ _ __ ___ ___ | |__ _ _ | | _____ _ __ _ __ _ _| |___ __ #
|
||||
# | |\\/| |/ _` | | '_ \\| __/ _ \\ '_ \\ / _` | '_ \\ / __/ _ \\ | '_ \\| | | | | |/ / _ \\ '_ \\| '_ \\| | | | __\\ \\ / / #
|
||||
# | | | | (_| | | | | | || __/ | | | (_| | | | | (_| __/ | |_) | |_| | | < __/ | | | | | | |_| | |_ \\ V / #
|
||||
# |_| |_|\\__,_|_|_| |_|\\__\\___|_| |_|\\__,_|_| |_|\\___\\___| |_.__/ \\__, | |_|\\_\\___|_| |_|_| |_|\\__, |\\__| \\_/ #
|
||||
# |___/ |___/ #
|
||||
###################################################################################################################
|
||||
# You can report bugs here: https://github.com/kennytv/Maintenance/issues
|
||||
# If you need any other help/support, you can also join my Discord server: https://discord.gg/vGCUzHq
|
||||
# The config and language files use MiniMessage, NOT legacy text for input. Use https://webui.advntr.dev/ to edit and preview the formatted text.
|
||||
# For a full list of formats and fancy examples of MiniMessage, see https://docs.papermc.io/adventure/minimessage/format/
|
||||
""";
|
||||
private final Yaml yaml = createYaml();
|
||||
private final File file;
|
||||
private final Set<String> unsupportedFields;
|
||||
@@ -61,11 +61,11 @@ public final class Config extends ConfigSection {
|
||||
public Config(final File file, final String... unsupportedFields) {
|
||||
super(null, "");
|
||||
this.file = file;
|
||||
this.unsupportedFields = unsupportedFields.length == 0 ? Collections.emptySet() : Sets.newHashSet(unsupportedFields);
|
||||
this.unsupportedFields = unsupportedFields.length == 0 ? Collections.emptySet() : Set.of(unsupportedFields);
|
||||
}
|
||||
|
||||
public void load() throws IOException {
|
||||
final String data = new String(Files.readAllBytes(this.file.toPath()), StandardCharsets.UTF_8);
|
||||
final String data = Files.readString(this.file.toPath());
|
||||
final Map<String, Object> map = yaml.load(data);
|
||||
this.values = map != null ? map : new LinkedHashMap<>();
|
||||
this.comments = ConfigSerializer.deserializeComments(data);
|
||||
@@ -101,12 +101,11 @@ public final class Config extends ConfigSection {
|
||||
}
|
||||
|
||||
public void saveTo(final File file) throws IOException {
|
||||
final byte[] bytes = toString().getBytes(StandardCharsets.UTF_8);
|
||||
if (file.getParentFile() != null) {
|
||||
file.getParentFile().mkdirs();
|
||||
}
|
||||
file.createNewFile();
|
||||
Files.write(file.toPath(), bytes);
|
||||
Files.writeString(file.toPath(), toString());
|
||||
}
|
||||
|
||||
public void replaceComments(final Config fromConfig) {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.config;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -36,7 +35,7 @@ public class ConfigSection {
|
||||
protected Map<String, Object> values;
|
||||
|
||||
ConfigSection(final Config root, final String currentPath) {
|
||||
this(root, currentPath, new HashMap<>());
|
||||
this(root, currentPath, new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
ConfigSection(final Config root, final String currentPath, final Map<String, Object> values) {
|
||||
@@ -63,20 +62,18 @@ public class ConfigSection {
|
||||
|
||||
@Nullable
|
||||
public Object getObject(final String key, final Object def) {
|
||||
int nextSeparatorIndex = -1;
|
||||
int sectionStartIndex;
|
||||
final String[] parts = key.split("\\.");
|
||||
ConfigSection section = this;
|
||||
while ((nextSeparatorIndex = key.indexOf('.', sectionStartIndex = nextSeparatorIndex + 1)) != -1) {
|
||||
section = section.getSection(key.substring(sectionStartIndex, nextSeparatorIndex));
|
||||
if (section == null) return def;
|
||||
for (int i = 0; i < parts.length - 1; i++) {
|
||||
section = section.getSection(parts[i]);
|
||||
if (section == null) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
final String subKey = key.substring(sectionStartIndex);
|
||||
if (section == this) {
|
||||
final Object result = values.get(subKey);
|
||||
return result != null ? result : def;
|
||||
}
|
||||
return section.getObject(subKey, def);
|
||||
final String valueKey = parts[parts.length - 1];
|
||||
final Object o = section.values.get(valueKey);
|
||||
return o != null ? o : def;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -164,7 +161,7 @@ public class ConfigSection {
|
||||
@Nullable
|
||||
public String getString(final String key, @Nullable final String def) {
|
||||
final Object o = getObject(key);
|
||||
return o instanceof String ? (String) o : def;
|
||||
return o instanceof String s ? s : def;
|
||||
}
|
||||
|
||||
public int getInt(final String key) {
|
||||
@@ -173,7 +170,7 @@ public class ConfigSection {
|
||||
|
||||
public int getInt(final String key, final int def) {
|
||||
final Object o = getObject(key);
|
||||
return o instanceof Number ? ((Number) o).intValue() : def;
|
||||
return o instanceof Number number ? number.intValue() : def;
|
||||
}
|
||||
|
||||
public double getDouble(final String key) {
|
||||
@@ -182,7 +179,7 @@ public class ConfigSection {
|
||||
|
||||
public double getDouble(final String key, final double def) {
|
||||
final Object o = getObject(key);
|
||||
return o instanceof Number ? ((Number) o).doubleValue() : def;
|
||||
return o instanceof Number number ? number.doubleValue() : def;
|
||||
}
|
||||
|
||||
public long getLong(final String key) {
|
||||
@@ -191,7 +188,7 @@ public class ConfigSection {
|
||||
|
||||
public long getLong(final String key, final long def) {
|
||||
final Object o = getObject(key);
|
||||
return o instanceof Number ? ((Number) o).longValue() : def;
|
||||
return o instanceof Number number ? number.longValue() : def;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -253,6 +250,6 @@ public class ConfigSection {
|
||||
}
|
||||
|
||||
protected String getFullKeyInPath(final String key) {
|
||||
return currentPath.isEmpty() ? key : currentPath + "." + key;
|
||||
return currentPath.isEmpty() ? key : currentPath + '.' + key;
|
||||
}
|
||||
}
|
||||
@@ -146,11 +146,6 @@ public final class ConfigSerializer {
|
||||
multiLineValue = false;
|
||||
}
|
||||
|
||||
// YAML list items are values, not keys - skip without touching key/indent state
|
||||
if (line.length() > indent && line.charAt(indent) == '-') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this is a level lower
|
||||
if (indents <= currentIndents) {
|
||||
final String[] array = key.split(PATH_SEPARATOR_QUOTED);
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.config;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public final class DisplayedMessages {
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
private final Map<String, List<String>> messages;
|
||||
private final List<String> defaultMessages;
|
||||
|
||||
public DisplayedMessages(final Map<String, List<String>> messages) {
|
||||
this.messages = messages;
|
||||
this.defaultMessages = messages.get("default");
|
||||
}
|
||||
|
||||
public List<String> getMessages(@Nullable final String key) {
|
||||
Preconditions.checkNotNull(defaultMessages, "no default message configured in messages list");
|
||||
if (key == null || messages.size() == 1) {
|
||||
return defaultMessages;
|
||||
}
|
||||
return messages.getOrDefault(key, defaultMessages);
|
||||
}
|
||||
|
||||
public String getRandomEntry(@Nullable final String key) {
|
||||
final List<String> messages = getMessages(key);
|
||||
return messages.get(RANDOM.nextInt(messages.size()));
|
||||
}
|
||||
|
||||
public List<String> getDefaultMessages() {
|
||||
return defaultMessages;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return messages.isEmpty();
|
||||
}
|
||||
|
||||
public Set<String> getKeys() {
|
||||
return messages.keySet();
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import eu.kennytv.maintenance.core.Settings;
|
||||
import java.io.File;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@SuppressWarnings({"FieldCanBeLocal", "unused"})
|
||||
public final class MaintenanceDump {
|
||||
@@ -35,12 +36,8 @@ public final class MaintenanceDump {
|
||||
general = new ServerDump(plugin.getVersion(), plugin.getServerType().toString(), plugin.getServerVersion(), plugin.getMaintenanceServersDump());
|
||||
|
||||
configuration = new LinkedHashMap<>(settings.getConfig().getValues());
|
||||
final Object o = configuration.get("mysql");
|
||||
if (o instanceof Map) {
|
||||
final Map<String, Object> map = new LinkedHashMap<>(((Map<String, Object>) o));
|
||||
map.keySet().removeIf(key -> !key.equals("use-mysql") && !key.equals("update-interval"));
|
||||
configuration.put("mysql", map);
|
||||
}
|
||||
addDBSettings("redis", "enabled");
|
||||
redact("webhook", "url");
|
||||
configuration.put("whitelisted-players", settings.getWhitelistedPlayers());
|
||||
configuration.put("icon-exists", new File(plugin.getDataFolder(), "maintenance-icon.png").exists());
|
||||
|
||||
@@ -48,4 +45,28 @@ public final class MaintenanceDump {
|
||||
jsonObject.add("plugins", MaintenancePlugin.GSON.toJsonTree(plugin.getPlugins()));
|
||||
plugins = jsonObject;
|
||||
}
|
||||
|
||||
private void redact(final String key, final String... subKeys) {
|
||||
final Object o = configuration.get(key);
|
||||
if (o instanceof Map) {
|
||||
final Map<String, Object> map = new LinkedHashMap<>(((Map<String, Object>) o));
|
||||
for (final String subKey : subKeys) {
|
||||
final Object value = map.get(subKey);
|
||||
if (value != null) {
|
||||
map.put(subKey, value.toString().isEmpty() ? "" : "<redacted>");
|
||||
}
|
||||
}
|
||||
configuration.put(key, map);
|
||||
}
|
||||
}
|
||||
|
||||
private void addDBSettings(final String key, final String... allowedSubKeys) {
|
||||
final Set<String> allowedSubKeysSet = Set.of(allowedSubKeys);
|
||||
final Object o = configuration.get(key);
|
||||
if (o instanceof Map) {
|
||||
final Map<String, Object> map = new LinkedHashMap<>(((Map<String, Object>) o));
|
||||
map.keySet().removeIf(subKey -> !allowedSubKeysSet.contains(subKey));
|
||||
configuration.put(key, map);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,12 +19,7 @@ package eu.kennytv.maintenance.core.dump;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings({"FieldCanBeLocal", "unused"})
|
||||
public final class PluginDump {
|
||||
private final String name;
|
||||
private final String version;
|
||||
private final List<String> authors;
|
||||
|
||||
public record PluginDump(String name, String version, List<String> authors) {
|
||||
public PluginDump(final String name, final String version, final List<String> authors) {
|
||||
this.name = name;
|
||||
this.version = version;
|
||||
|
||||
@@ -19,17 +19,5 @@ package eu.kennytv.maintenance.core.dump;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings({"FieldCanBeLocal", "unused"})
|
||||
public final class ServerDump {
|
||||
private final String pluginVersion;
|
||||
private final String platform;
|
||||
private final String serverVersion;
|
||||
private final List<String> maintenance;
|
||||
|
||||
public ServerDump(final String pluginVersion, final String platform, final String serverVersion, final List<String> maintenance) {
|
||||
this.pluginVersion = pluginVersion;
|
||||
this.platform = platform;
|
||||
this.serverVersion = serverVersion;
|
||||
this.maintenance = maintenance;
|
||||
}
|
||||
public record ServerDump(String pluginVersion, String platform, String serverVersion, List<String> maintenance) {
|
||||
}
|
||||
@@ -24,10 +24,10 @@ public final class ServerListPlusHook {
|
||||
private final ServerListPlusCore serverListPlusCore;
|
||||
|
||||
public ServerListPlusHook(final Object serverListPlus) {
|
||||
if (!(serverListPlus instanceof ServerListPlusPlugin)) {
|
||||
if (!(serverListPlus instanceof ServerListPlusPlugin serverListPlusPlugin)) {
|
||||
throw new IllegalArgumentException("Couldn't parse ServerListPlus instance!");
|
||||
}
|
||||
this.serverListPlusCore = ((ServerListPlusPlugin) serverListPlus).getCore();
|
||||
this.serverListPlusCore = serverListPlusPlugin.getCore();
|
||||
}
|
||||
|
||||
public void setEnabled(final boolean enable) {
|
||||
|
||||
@@ -19,17 +19,19 @@ package eu.kennytv.maintenance.core.listener;
|
||||
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.Settings;
|
||||
import eu.kennytv.maintenance.core.util.DiscordWebhook;
|
||||
import eu.kennytv.maintenance.core.util.SenderInfo;
|
||||
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.TextComponent;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.event.ClickEvent;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.event.HoverEvent;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.format.NamedTextColor;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.format.TextDecoration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
|
||||
import static eu.kennytv.maintenance.core.util.ComponentUtil.clickEvent;
|
||||
|
||||
public abstract class JoinListenerBase {
|
||||
protected final MaintenancePlugin plugin;
|
||||
@@ -48,13 +50,16 @@ public abstract class JoinListenerBase {
|
||||
* @return true if the sender should be kicked
|
||||
*/
|
||||
protected boolean shouldKick(final SenderInfo sender, final boolean updateCheck) {
|
||||
if (!settings.isMaintenance() || sender.hasMaintenancePermission("bypass") || settings.isWhitelisted(sender.getUuid())) {
|
||||
if (!settings.isMaintenance() || sender.hasMaintenancePermission("bypass") || settings.isWhitelisted(sender.uuid())) {
|
||||
if (updateCheck) {
|
||||
updateCheck(sender);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (settings.isWebhookEnabled()) {
|
||||
plugin.sendWebhookMessage("webhookJoinNotification", DiscordWebhook.EventType.BLOCKED_JOIN, "%PLAYER%", sender.name());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -64,19 +69,19 @@ public abstract class JoinListenerBase {
|
||||
|
||||
protected void updateCheck(final SenderInfo sender) {
|
||||
if (!settings.hasUpdateChecks()) return;
|
||||
if (!sender.hasPermission("maintenance.admin") || notifiedPlayers.contains(sender.getUuid())) return;
|
||||
if (!sender.hasPermission("maintenance.admin") || notifiedPlayers.contains(sender.uuid())) return;
|
||||
plugin.async(() -> {
|
||||
if (!plugin.updateAvailable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
notifiedPlayers.add(sender.getUuid());
|
||||
notifiedPlayers.add(sender.uuid());
|
||||
sender.sendPrefixedRich("<red>There is a newer version available: <green>Version " + plugin.getNewestVersion() + "<red>, you're on <green>" + plugin.getVersion());
|
||||
|
||||
final TextComponent text = Component.text().content("Download it at: ").color(NamedTextColor.RED)
|
||||
.append(Component.text().content(MaintenancePlugin.HANGAR_URL).color(NamedTextColor.GOLD))
|
||||
.append(Component.text().content(" (CLICK ME)").color(NamedTextColor.GRAY).decorate(TextDecoration.BOLD))
|
||||
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, MaintenancePlugin.HANGAR_URL))
|
||||
.clickEvent(clickEvent(ClickEvent.Action.OPEN_URL, MaintenancePlugin.HANGAR_URL))
|
||||
.hoverEvent(HoverEvent.showText(Component.text("Download the latest version").color(NamedTextColor.GREEN)))
|
||||
.build();
|
||||
sender.send(plugin.prefix().append(text));
|
||||
|
||||
@@ -19,17 +19,24 @@ package eu.kennytv.maintenance.core.runnable;
|
||||
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.Settings;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class MaintenanceRunnable extends MaintenanceRunnableBase {
|
||||
protected final String mode;
|
||||
|
||||
public MaintenanceRunnable(final MaintenancePlugin plugin, final Settings settings, final int seconds, final boolean enable) {
|
||||
this(plugin, settings, seconds, enable, null);
|
||||
}
|
||||
|
||||
public MaintenanceRunnable(final MaintenancePlugin plugin, final Settings settings, final int seconds, final boolean enable, @Nullable final String mode) {
|
||||
super(plugin, settings, seconds, enable);
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finish() {
|
||||
plugin.setMaintenance(enable);
|
||||
plugin.setMaintenance(enable, mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ package eu.kennytv.maintenance.core.runnable;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.Settings;
|
||||
import eu.kennytv.maintenance.core.util.Task;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public abstract class MaintenanceRunnableBase implements Runnable {
|
||||
protected final MaintenancePlugin plugin;
|
||||
|
||||
+7
-2
@@ -19,14 +19,19 @@ package eu.kennytv.maintenance.core.runnable;
|
||||
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.core.Settings;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import java.time.Duration;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class MaintenanceScheduleRunnable extends MaintenanceRunnable {
|
||||
private final int maintenanceDuration;
|
||||
|
||||
public MaintenanceScheduleRunnable(final MaintenancePlugin plugin, final Settings settings, final int secondsToEnable, final int maintenanceDuration) {
|
||||
super(plugin, settings, secondsToEnable, true);
|
||||
this(plugin, settings, secondsToEnable, maintenanceDuration, null);
|
||||
}
|
||||
|
||||
public MaintenanceScheduleRunnable(final MaintenancePlugin plugin, final Settings settings, final int secondsToEnable, final int maintenanceDuration, @Nullable final String mode) {
|
||||
super(plugin, settings, secondsToEnable, true, mode);
|
||||
this.maintenanceDuration = maintenanceDuration;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* This file is part of Maintenance - https://github.com/kennytv/Maintenance
|
||||
* Copyright (C) 2018-2024 kennytv (https://github.com/kennytv)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.util;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public final class ComponentUtil {
|
||||
|
||||
private static final MethodHandle LEGACY_CLICK_EVENT = legacyClickEvent();
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public static ClickEvent clickEvent(final ClickEvent.Action action, final String value) {
|
||||
if (LEGACY_CLICK_EVENT != null) {
|
||||
try {
|
||||
return (ClickEvent) LEGACY_CLICK_EVENT.invoke(action, value);
|
||||
} catch (final Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return ClickEvent.clickEvent(action, ClickEvent.Payload.string(value));
|
||||
}
|
||||
|
||||
private static @Nullable MethodHandle legacyClickEvent() {
|
||||
try {
|
||||
return MethodHandles.publicLookup().unreflect(ClickEvent.class.getDeclaredMethod("clickEvent", ClickEvent.Action.class, String.class));
|
||||
} catch (final NoSuchMethodException | IllegalAccessException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package eu.kennytv.maintenance.core.util;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import eu.kennytv.maintenance.core.Settings;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
|
||||
public final class DiscordWebhook {
|
||||
|
||||
private static final int COLOR_MAINTENANCE_ENABLED = 0xED4245; // Red
|
||||
private static final int COLOR_MAINTENANCE_DISABLED = 0x57F287; // Green
|
||||
private static final int COLOR_STARTTIMER = 0xFEE75C; // Yellow
|
||||
private static final int COLOR_ENDTIMER = 0x5865F2; // Blue
|
||||
private static final int COLOR_BLOCKED_JOIN = 0xE67E22; // Orange
|
||||
|
||||
public static void sendMessage(final String message, final EventType event, final Settings settings) throws IOException, InterruptedException {
|
||||
final JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("username", settings.getWebhookUsername());
|
||||
|
||||
final String avatarUrl = settings.getWebhookAvatarUrl();
|
||||
if (!avatarUrl.isEmpty()) {
|
||||
jsonObject.addProperty("avatar_url", avatarUrl);
|
||||
}
|
||||
|
||||
final JsonObject embed = new JsonObject();
|
||||
embed.addProperty("description", message);
|
||||
embed.addProperty("color", getColorForEvent(event));
|
||||
|
||||
final JsonArray embeds = new JsonArray();
|
||||
embeds.add(embed);
|
||||
jsonObject.add("embeds", embeds);
|
||||
|
||||
try (final HttpClient client = HttpClient.newHttpClient()) {
|
||||
final HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(settings.getWebhookUrl()))
|
||||
.header("Content-Type", "application/json")
|
||||
.POST(HttpRequest.BodyPublishers.ofString(jsonObject.toString()))
|
||||
.build();
|
||||
client.send(request, HttpResponse.BodyHandlers.discarding());
|
||||
}
|
||||
}
|
||||
|
||||
private static int getColorForEvent(final EventType event) {
|
||||
return switch (event) {
|
||||
case MAINTENANCE_ENABLED -> COLOR_MAINTENANCE_ENABLED;
|
||||
case MAINTENANCE_DISABLED -> COLOR_MAINTENANCE_DISABLED;
|
||||
case STARTTIMER_STARTED -> COLOR_STARTTIMER;
|
||||
case ENDTIMER_STARTED -> COLOR_ENDTIMER;
|
||||
case BLOCKED_JOIN -> COLOR_BLOCKED_JOIN;
|
||||
};
|
||||
}
|
||||
|
||||
public enum EventType {
|
||||
MAINTENANCE_ENABLED("maintenance_status"),
|
||||
MAINTENANCE_DISABLED("maintenance_status"),
|
||||
STARTTIMER_STARTED("starttimer_started"),
|
||||
ENDTIMER_STARTED("endtimer_started"),
|
||||
BLOCKED_JOIN("blocked_join");
|
||||
|
||||
private final String configKey;
|
||||
|
||||
EventType(final String configKey) {
|
||||
this.configKey = configKey;
|
||||
}
|
||||
|
||||
public String configKey() {
|
||||
return configKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,28 +17,10 @@
|
||||
*/
|
||||
package eu.kennytv.maintenance.core.util;
|
||||
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
|
||||
import java.util.UUID;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public final class DummySenderInfo implements SenderInfo {
|
||||
private final UUID uuid;
|
||||
private final String name;
|
||||
|
||||
public DummySenderInfo(final UUID uuid, final String name) {
|
||||
this.uuid = uuid;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public record DummySenderInfo(UUID uuid, String name) implements SenderInfo {
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(final String permission) {
|
||||
|
||||
@@ -19,15 +19,15 @@ package eu.kennytv.maintenance.core.util;
|
||||
|
||||
import eu.kennytv.maintenance.api.MaintenanceProvider;
|
||||
import eu.kennytv.maintenance.core.MaintenancePlugin;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.Component;
|
||||
import eu.kennytv.maintenance.lib.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import java.util.UUID;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
|
||||
public interface SenderInfo {
|
||||
|
||||
UUID getUuid();
|
||||
UUID uuid();
|
||||
|
||||
String getName();
|
||||
String name();
|
||||
|
||||
boolean hasPermission(String permission);
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ package eu.kennytv.maintenance.core.util;
|
||||
public enum ServerType {
|
||||
|
||||
BUNGEE("Bungee"),
|
||||
SPIGOT("Spigot"),
|
||||
PAPER("Paper"),
|
||||
SPONGE("Sponge"),
|
||||
VELOCITY("Velocity");
|
||||
|
||||
|
||||
@@ -89,8 +89,7 @@ public final class Version implements Comparable<Version> {
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (o == this) return true;
|
||||
if (!(o instanceof Version)) return false;
|
||||
final Version other = (Version) o;
|
||||
if (!(o instanceof Version other)) return false;
|
||||
return version.equals(other.version);
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@
|
||||
###################################################################################################################
|
||||
# You can report bugs here: https://github.com/kennytv/Maintenance/issues
|
||||
# If you need any other help/support, you can also join my Discord server: https://discord.gg/vGCUzHq
|
||||
# The config and language files use MiniMessage, NOT legacy text for input. Use https://webui.adventure.kyori.net/ to edit and preview the formatted text.
|
||||
# For a full list of formats and fancy examples of MiniMessage, see https://docs.adventure.kyori.net/minimessage/format.html
|
||||
# The config and language files use MiniMessage, NOT legacy text for input. Use https://webui.advntr.dev/ to edit and preview the formatted text.
|
||||
# For a full list of formats and fancy examples of MiniMessage, see https://docs.papermc.io/adventure/minimessage/format/
|
||||
|
||||
# Changes the language of command feedback/messages.
|
||||
# If you find missing translations or want to contribute, see https://crowdin.com/project/maintenance
|
||||
@@ -18,16 +18,26 @@
|
||||
language: en
|
||||
|
||||
# Enables maintenance mode.
|
||||
maintenance-enabled: true
|
||||
maintenance-enabled: false
|
||||
|
||||
# The currently active maintenance mode key (used for mode-specific messages/icons).
|
||||
# Use 'default' or leave empty to use fallback messages.
|
||||
active-mode: default
|
||||
|
||||
# Value for the %REASON% placeholder in maintenance messages.
|
||||
# This can be changed via /maintenance setreason <text>.
|
||||
active-reason: ""
|
||||
|
||||
# Enables maintenance on certain servers managed by your proxy instance(s).
|
||||
# The value is the mode key used for ping/player count messages for that server.
|
||||
proxied-maintenance-servers:
|
||||
# - AServer
|
||||
PaperServer1: default
|
||||
# AnotherServer: release
|
||||
|
||||
# If maintenance is enabled on a proxied server with players on it, they will be sent to the fallback server instead of being kicked off the network.
|
||||
# If the fallback server is not reachable/not set, they will just be kicked off the network.
|
||||
# This can also be set to an array to set multiple fallback servers, in case one server is not always reachable: 'fallback: [s1, s2, s3, ...]'
|
||||
fallback: unreachable
|
||||
fallback: lobby
|
||||
|
||||
# If maintenance is enabled globally, you can define a "waiting" lobby for players to be sent to instead of being kicked.
|
||||
# Leave this empty to disable.
|
||||
@@ -35,35 +45,53 @@ waiting-server: ""
|
||||
|
||||
# The message (MOTD) shown in the multiplayer server list motd when maintenance is enabled.
|
||||
# If you put in multiple entries, one of them will be chosen randomly on every ping.
|
||||
# If running an endtimer, the time left can be displayed by including '%TIMER%' in a ping message.
|
||||
# The type of mode can be defined in the command, or will be the default mode.
|
||||
ping-message:
|
||||
enabled: true
|
||||
# Always uses 'default' if not defined in the command or not present in the config.
|
||||
# Additional examples could look as follows:
|
||||
# default:
|
||||
# - "<red>Currently under maintenance<br><gradient:#fbffc2:#0fffff>We will be back soon!"
|
||||
# - "<red>Wooow you found our secondary MOTD!"
|
||||
# reason:
|
||||
# - "<red>Currently under maintenance<br><gradient:#fbffc2:#0fffff>%REASON%"
|
||||
# release:
|
||||
# - "<gold>We're currently preparing the release, come back soon!"
|
||||
messages:
|
||||
- "<bold><green>True<red>OG</bold> <yellow>Network</yellow> <aqua>》<red> Updating... </red><aqua>《 <gold>[1.8.0-1.21.11]<br><yellow>⚠ <dark_red>Maintenance in progress! <yellow>We'll be back <dark_aqua>soon™"
|
||||
default:
|
||||
- "<red>Currently under maintenance<br><gradient:#fbffc2:#0fffff>We will be back soon!"
|
||||
# If set to true and an endtimer is currently running, a message from this pool will be chosen
|
||||
# instead of the ones above, so you can have different messages for when an endtimer is running/not running.
|
||||
enable-timer-specific-messages: true
|
||||
# If running an endtimer, the time left can be displayed by including '%TIMER%' in a ping message.
|
||||
timer-messages:
|
||||
- "<bold><green>True<red>OG</bold> <yellow>Network</yellow> <aqua>》<red>Maintenance Mode</red><aqua>《 <gold>[1.8.0-1.21.11]<br><dark_red>Maintenance ends in <yellow>%TIMER% <gray>| <green>Thanks for waiting!"
|
||||
default:
|
||||
- "<red>Currently under maintenance<br><gradient:#fbffc2:#fffff>Come back in:</gradient> <color:#aa55ee>%TIMER%"
|
||||
|
||||
# If enabled, the message below will be shown in the top right corner of the server in the server list, where the player count would normally be displayed.
|
||||
# Same as the ping-message, you can define messages for different modes.
|
||||
# You can use '%ONLINE%' and '%MAX%' if you want to include the player count in a custom message (e.g. "Maintenance %ONLINE%/%MAX%").
|
||||
# DOES NOT SUPPORT RGB!
|
||||
player-count-message:
|
||||
enabled: true
|
||||
message: "<dark_red>Maintenance"
|
||||
messages:
|
||||
default: "<dark_red>Maintenance"
|
||||
# If set to true and an endtimer is currently running, the timer message will be used instead of the normal one.
|
||||
enable-timer-specific-message: true
|
||||
timer-message: "<dark_red>Come back in: <yellow>%TIMER%"
|
||||
enable-timer-specific-message: false
|
||||
timer-messages:
|
||||
default: "<dark_red>Come back in: <yellow>%TIMER%"
|
||||
|
||||
# Is shown when you move your mouse above the text in the top right corner of the server in the server list, where the player count would normally be displayed.
|
||||
# Same as the ping-message, you can define messages for different modes.
|
||||
# DOES NOT SUPPORT RGB!
|
||||
player-list-hover-message:
|
||||
enabled: true
|
||||
message: "<red>Currently under<br><red>maintenance"
|
||||
messages:
|
||||
default: "<red>Currently under<br><red>maintenance"
|
||||
# If set to true and an endtimer is currently running, the timer message will be used instead of the normal one.
|
||||
enable-timer-specific-message: true
|
||||
timer-message: "<red>Come back in:<br><red><yellow>%TIMER%"
|
||||
enable-timer-specific-message: false
|
||||
timer-messages:
|
||||
default: "<red>Come back in:<br><red><yellow>%TIMER%"
|
||||
|
||||
# Any extra commands inside the arrays will be executed when maintenance is enabled/disabled.
|
||||
# Example: commands-on-maintenance-enable: ["say hello!", "stop"]
|
||||
@@ -84,7 +112,7 @@ custom-maintenance-icon: false
|
||||
|
||||
# If set to true, players with the 'maintenance.joinnotification' permission will receive a message,
|
||||
# that a player tried to join the server while maintenance is enabled.
|
||||
send-join-notification: true
|
||||
send-join-notification: false
|
||||
|
||||
# Set this to false if you do not want players to be kicked when you enable maintenance (new connections will still be blocked).
|
||||
kick-online-players: true
|
||||
@@ -103,28 +131,32 @@ continue-endtimer-after-restart:
|
||||
# If using the timer command: In what intervalls before enabling/disabling maintenance there will be a broadcast.
|
||||
timer-broadcast-for-seconds: [1200, 900, 600, 300, 120, 60, 30, 20, 10, 5, 4, 3, 2, 1]
|
||||
|
||||
# If you have 2 or more proxy instances, the database connection will make sure all proxies
|
||||
# have the same maintenance status, so you won't have to enable/disable it on every proxy by hand.
|
||||
mysql:
|
||||
use-mysql: false
|
||||
host: host
|
||||
port: 3306
|
||||
database: database
|
||||
username: username
|
||||
password: password
|
||||
# Requests to the database will only be made at least x seconds after the last request,
|
||||
# so there won't be a request on every single ping on a server with many players.
|
||||
#
|
||||
# Set this to 0 to make a database request on (almost) every single ping.
|
||||
# (0 is not recommended if having more than a hundred concurrent players, then I'd recommend keeping it from 10-60).
|
||||
update-interval: 15
|
||||
# If you for some reason cannot establish SSL connections to your database, you may disable it here (not recommended).
|
||||
use-ssl: true
|
||||
# Allows sending a Discord webhook on certain events.
|
||||
webhook:
|
||||
enabled: false
|
||||
# Possible values: maintenance_status, starttimer_started, endtimer_started, blocked_join
|
||||
enabled-events: ["maintenance_status", "starttimer_started", "endtimer_started"]
|
||||
# Copy and paste the webhook url from Server Settings --> Apps Integrations --> Webhooks
|
||||
url: ""
|
||||
# The username displayed for the webhook bot message.
|
||||
username: "Maintenance"
|
||||
# The avatar URL used for the webhook bot message. Leave empty to use the default Discord webhook avatar.
|
||||
avatar-url: ""
|
||||
|
||||
# If you have 2 or more proxy instances, the Redis connection will make sure all proxies
|
||||
# have the same maintenance status (global as well as per server) and whitelisted players,
|
||||
# so you won't have to update these on every proxy by hand.
|
||||
redis:
|
||||
enabled: false
|
||||
# Make sure your Redis instance is properly protected.
|
||||
# URI format: redis[s]://[[username][:password]@][host][:port][/db-number]
|
||||
# E.g. an SSL connection to a password protected redis could look like this: "rediss://user:password@127.0.0.1:6379"
|
||||
uri: "redis://localhost:6379"
|
||||
|
||||
# If disabled, you will no longer receive any messages if there is an update.
|
||||
# Not recommended to disable, as new versions generally tend to run better and with fewer bugs.
|
||||
# However, you can always check for updates manually using the '/maintenance update' command.
|
||||
update-checks: false
|
||||
update-checks: true
|
||||
|
||||
# Used for autoupdating the config, do not change this value.
|
||||
config-version: 9
|
||||
config-version: 12
|
||||
@@ -0,0 +1,124 @@
|
||||
#You can use https://webui.advntr.dev/ to edit and preview the formatted text
|
||||
#For a full list of formats and fancy examples, see https://docs.papermc.io/adventure/minimessage/format/
|
||||
#If you want to contribute changes to the default messages, you can do so via https://crowdin.com/project/maintenance
|
||||
#Messages containing the placeholder "<prefix>" will have it replaced with the following string
|
||||
prefix: "<dark_gray>[<yellow>Maintenance<dark_gray>] "
|
||||
noPermission: "<red>No tens permís per executar aquesta ordre."
|
||||
kickmessage: "<red>El servidor està en manteniment!<br>Torna-ho a provar més tard!"
|
||||
maintenanceActivated: "<prefix><gold>El mode de manteniment s'ha activat."
|
||||
maintenanceDeactivated: "<prefix><gold>El mode de manteniment s'ha desactivat."
|
||||
maintenanceStatusOn: "<prefix><gold>Maintenance mode is currently <green>enabled<gold>."
|
||||
maintenanceStatusOff: "<prefix><gold>Maintenance mode is currently <red>disabled<gold>."
|
||||
maintenanceStatusOnWithEndTimer: "<prefix><gold>Maintenance mode is currently <green>enabled <grey>and will be disabled in <gold>%TIME%."
|
||||
maintenanceStatusOffWithStartTimer: "<prefix><gold>Maintenance mode is currently <red>disabled <grey>and will be enabled in <gold>%TIME%."
|
||||
alreadyEnabled: "<prefix><red>El manteniment ja està activat!"
|
||||
alreadyDisabled: "<prefix><red>El manteniment ja està desactivat!"
|
||||
endtimerBroadcast: "<prefix><gray>El mode de manteniment es desactivarà en <gold>%TIME%<gray>."
|
||||
endtimerStarted: "<prefix><green>Temporitzador iniciat: <gray>El mode de manteniment es desactivarà en <gold>%TIME%<gray>."
|
||||
starttimerBroadcast: "<prefix><gray>El mode de manteniment s'activarà en <gold>%TIME%<gray>."
|
||||
starttimerStarted: "<prefix><green>Temporitzador iniciat: <gray>El mode de manteniment s'activarà en <gold>%TIME%<gray>."
|
||||
scheduletimerBroadcast: "<prefix><gray>El mode de manteniment s'activarà en <gold>%TIME% <gray>i durarà <gold>%DURATION%<gray>."
|
||||
scheduletimerStarted: "<prefix><green>Temporitzador iniciat: <gray>El mode de manteniment s'activarà en <gold>%TIME% <gray>i durarà <gold>%DURATION%<gray>."
|
||||
timerAlreadyRunning: "<prefix><red>Ja hi ha un temporitzador programat!"
|
||||
timerNotRunning: "<prefix><red>No hi ha cap temporitzador en marxa."
|
||||
timerCancelled: "<prefix><red>S'ha cancel·lat el temporitzador actual."
|
||||
timerTooLong: "<prefix><red>La durada ha de ser inferior a 28 dies!"
|
||||
timerMotdDisabled: "<prefix><red>Has de configurar 'enable-timerspecific-messages' a 'true' al fitxer de configuració per utilitzar/editar missatges MOTD específics del temporitzador."
|
||||
motdTimer: "%HOURS%:%MINUTES%:%SECONDS%"
|
||||
motdTimerNotRunning: "-"
|
||||
joinNotification: "<prefix><yellow>%PLAYER% <red>ha intentat unir-se al servidor."
|
||||
motdList: "<prefix><gray>Llista dels teus missatges MOTD de manteniment:"
|
||||
motdListEmpty: "<prefix><red>No tens cap missatge MOTD de manteniment configurat!"
|
||||
reload: "<prefix><green>S'han recarregat la configuració, la llista de jugadors permitits, el fitxer d'idioma i la icona de manteniment."
|
||||
removedMotd: "<prefix><green>S'ha eliminat el missatge MOTD número %INDEX%."
|
||||
removeMotdError: "<prefix><red>Només tens un missatge MOTD, no en pots eliminar cap!"
|
||||
setMotd: "<prefix><green>S'ha configurat la línia %LINE% del missatge MOTD de manteniment %INDEX% a %MOTD%"
|
||||
setReason: "<prefix><green>Set maintenance reason to: <yellow>%REASON%"
|
||||
setMotdIndexError: "<prefix><red>Actualment tens %MOTDS% missatges MOTD, has de triar un número entre 1 i %NEWAMOUNT%."
|
||||
setMotdLineError: "<prefix><red>El segon argument ha de ser el número de línia (1 o 2)!"
|
||||
updateDownloading: "<prefix><red><bold>S'està descarregant l'actualització..."
|
||||
updateFailed: "<prefix><dark_red><bold>L'actualització ha fallat!"
|
||||
updateFinished: "<prefix><green><bold>L'actualització s'ha completat! Per evitar problemes amb tasques i completar l'actualització, has de reiniciar el servidor!"
|
||||
whitelistedPlayers: "<prefix><gold>Jugadors permesos durant el manteniment:"
|
||||
whitelistedPlayersFormat: "<dark_gray>- <yellow>%NAME% <dark_gray>(<gray>%UUID%<dark_gray>)"
|
||||
whitelistAdded: "<prefix><green>S'ha afegit <aqua>%PLAYER% <green>a la llista blanca de manteniment!"
|
||||
whitelistAlreadyAdded: "<prefix><aqua>%PLAYER% <red>ja està a la llista blanca de manteniment!"
|
||||
whitelistRemoved: "<prefix><green>S'ha eliminat <aqua>%PLAYER% <green>de la llista blanca de manteniment!"
|
||||
whitelistNotFound: "<prefix><red>Aquest jugador no està a la llista blanca de manteniment!"
|
||||
whitelistEmpty: "<prefix><red>La llista blanca de manteniment està buida! Utilitza <yellow>/maintenance add <player/uuid> <red>per afegir algú!"
|
||||
playerNotFound: "<prefix><red>No s'ha trobat cap jugador amb aquest nom al servidor."
|
||||
playerNotFoundUuid: "<prefix><red>No s'ha trobat cap jugador amb aquesta UUID."
|
||||
playerNotOnline: "<prefix><red>No hi ha cap jugador en línia amb aquest nom."
|
||||
offlinePlayerFetchError: "<prefix><red>Hi ha hagut un error en obtenir les dades del jugador fora de línia. Torna-ho a provar més tard."
|
||||
invalidUuid: "<prefix><red>Format d'UUID no vàlid!"
|
||||
#Messages for the Bungee/Velocity part, you can ignore them if you use the plugin on Paper/Sponge
|
||||
sentToWaitingServer: "<prefix><red>Se t'ha enviat a un servidor d'espera!"
|
||||
forceWaitingServer: "<prefix><red>No pots abandonar el servidor d'espera mentre el manteniment estigui actiu!"
|
||||
serverNotFound: "<prefix><red>No hi ha cap servidor amb aquest nom registrat al proxy!"
|
||||
singleTimerAlreadyRunning: "<prefix><red>Ja hi ha un temporitzador programat per a aquest servidor!"
|
||||
singleTimerCancelled: "<prefix><red>S'ha cancel·lat el temporitzador actual per al servidor <yellow>%SERVER%<red>."
|
||||
singleTimerNotRunning: "<prefix><red>No hi ha cap temporitzador en marxa per a aquest servidor."
|
||||
singleEndtimerBroadcast: "<prefix><gray>El mode de manteniment al servidor %SERVER% es desactivarà en <gold>%TIME%<gray>."
|
||||
singleEndtimerStarted: "<prefix><green>Temporitzador iniciat: <gray>El mode de manteniment al servidor %SERVER% es desactivarà en <gold>%TIME%<gray>."
|
||||
singleStarttimerBroadcast: "<prefix><gray>El mode de manteniment al servidor %SERVER% s'activarà en <gold>%TIME%<gray>."
|
||||
singleStarttimerStarted: "<prefix><green>Temporitzador iniciat: <gray>El mode de manteniment al servidor %SERVER% s'activarà en <gold>%TIME%<gray>."
|
||||
singleScheduletimerBroadcast: "<prefix><gray>El mode de manteniment al servidor %SERVER% s'activarà en <gold>%TIME% <gray>i durarà <gold>%DURATION%<gray>."
|
||||
singleScheduletimerStarted: "<prefix><green>Temporitzador iniciat: <gray>El mode de manteniment al servidor %SERVER% s'activarà en <gold>%TIME% <gray>i durarà <gold>%DURATION%<gray>."
|
||||
singleMaintenanceKick: "<prefix><red>El servidor %SERVER% està en manteniment! Torna-ho a provar més tard!"
|
||||
singleMaintenanceKickComplete: "<red>El servidor %SERVER% està en manteniment!<br>Pots tornar a connectar-te per anar a un altre servidor!"
|
||||
singleMaintenanceActivated: "<prefix><gold>El mode de manteniment s'ha activat al servidor <yellow>%SERVER%<gold>."
|
||||
singleMaintenanceDeactivated: "<prefix><gold>El mode de manteniment s'ha desactivat al servidor <yellow>%SERVER%<gold>."
|
||||
singleServerAlreadyEnabled: "<prefix><red>El manteniment ja està activat al servidor <yellow>%SERVER%<red>!"
|
||||
singleServerAlreadyDisabled: "<prefix><red>El manteniment ja està desactivat al servidor <yellow>%SERVER%<red>!"
|
||||
singleServerMaintenanceList: "<prefix><gray>Servidors proxy que tenen el manteniment activat:"
|
||||
singleServerMaintenanceListEmpty: "<prefix><gray>No hi ha cap servidor proxy en manteniment."
|
||||
singleServerMaintenanceListEntry: "<dark_grey>- <aqua>%SERVER%"
|
||||
singleServerMaintenanceListEntryWithStartTimer: "<dark_grey>- <grey>%SERVER% <yellow>starting in <gold>%TIME%"
|
||||
singleServerMaintenanceListEntryWithEndTimer: "<dark_grey>- <aqua>%SERVER% <yellow>ending in <gold>%TIME%"
|
||||
second: "segon"
|
||||
seconds: "segons"
|
||||
minute: "minut"
|
||||
minutes: "minuts"
|
||||
hour: "hora"
|
||||
hours: "hores"
|
||||
helpHeader: "<dark_gray>========[ <yellow>%NAME% <dark_gray>| <yellow>%PAGE%/%MAX% <dark_gray>]========"
|
||||
helpPageNotFound: "<prefix><red>No hi ha cap pàgina amb aquest número!"
|
||||
helpNextPage: "<gray>Fes servir <aqua>/maintenance help %PAGE% <gray>per anar a la següent pàgina d'ajuda."
|
||||
helpAbortTimer: "<gold>/maintenance aborttimer <gray>(Si està actiu, cancel·la el temporitzador actual)"
|
||||
helpEndtimer: "<gold>/maintenance endtimer <desactivar en> <gray>(Després del temps indicat, p. ex. 1h5m, es desactivarà el manteniment)"
|
||||
helpStarttimer: "<gold>/maintenance starttimer <activar en> <gray>(Després del temps indicat, p. ex. 1h5m, s'activarà el manteniment)"
|
||||
helpScheduleTimer: "<gold>/maintenance scheduletimer <activar en> <durada del manteniment> <gray>(Després del temps indicat, p. ex. 1h5m, s'activarà el manteniment)"
|
||||
helpDebug: "<gold>/maintenance debug <gray>(Activa el registre de depuració)"
|
||||
helpDump: "<gold>/maintenance dump <gray>(Extreu informació del servidor, útil per informes d'errors)"
|
||||
helpHelp: "<gold>/maintenance help [pàgina] <gray>(Mostra aquesta finestra d'ajuda)"
|
||||
helpMotd: "<gold>/maintenance motd [timer] <gray>(Mostra els missatges MOTD de manteniment. Si s'especifica 'timer', es mostraran els MOTD del temporitzador)"
|
||||
helpRemoveMotd: "<gold>/maintenance removemotd [timer] <índex> <gray>(Elimina un missatge MOTD de manteniment. Si s'usa 'timer', s'eliminarà un missatge específic del temporitzador)"
|
||||
helpSetMotd: "<gold>/maintenance setmotd [timer] <índex> <1/2> <missatge> <gray>(Configura un missatge MOTD per al manteniment. Si s'usa 'timer', es configurarà un missatge específic del temporitzador)"
|
||||
helpSetReason: "<gold>/maintenance setreason <reason/none> <gray>(Sets or clears the %REASON% placeholder value)"
|
||||
helpReload: "<gold>/maintenance reload <gray>(Recarrega el fitxer de configuració, la llista de permitits i la icona del servidor)"
|
||||
helpGlobalStatus: "<gold>/maintenance status <gray>(Shows the current maintenance status and any running timers)"
|
||||
helpToggle: "<gold>/maintenance <on/off> [mode] <gray>(Enables/disables maintenance mode with an optional display mode when enabling)"
|
||||
helpUpdate: "<gold>/maintenance update <gray>(Descarrega la versió més recent del plugin al servidor)"
|
||||
helpWhitelist: "<gold>/maintenance whitelist <gray>(Mostra tots els jugadors permitits durant el manteniment)"
|
||||
helpWhitelistAdd: "<gold>/maintenance add <nom/uuid> <gray>(Afegeix el jugador a la llista de permitits del manteniment)"
|
||||
helpWhitelistRemove: "<gold>/maintenance remove <nom/uuid> <gray>(Elimina el jugador de la llista de permitits del manteniment)"
|
||||
#Messages for the Bungee/Velocity part, you can ignore them if you use the plugin on Paper/Sponge
|
||||
helpAbortSingleTimer: "<gold>/maintenance aborttimer [servidor] <gray>(Si està actiu, cancel·la el temporitzador del servidor)"
|
||||
helpSingleEndtimer: "<gold>/maintenance endtimer [server|global] <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpSingleStarttimer: "<gold>/maintenance starttimer [server|global] [mode] <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleScheduleTimer: "<gold>/maintenance scheduletimer [server|global] [mode] <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleToggle: "<gold>/maintenance <on/off> [server|global] [mode] <gray>(Enables/disables maintenance mode globally or on a specific server with an optional display mode when enabling)"
|
||||
helpStatus: "<gold>/maintenance status <gray>(Mostra tots els servidors proxy en manteniment)"
|
||||
#Discord webhook messages. These use Discord markdown, not MiniMessage.
|
||||
#For timer webhooks, %TIMESTAMP% is a Unix timestamp for use in Discord timestamp tags, e.g. <t:%TIMESTAMP%:R>.
|
||||
webhookMaintenanceActivated: "**Maintenance mode enabled.**"
|
||||
webhookMaintenanceDeactivated: "**Maintenance mode disabled.**"
|
||||
webhookStarttimerStarted: "Maintenance mode will be enabled <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookEndtimerStarted: "Maintenance mode will be disabled <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookJoinNotification: "Blocked join: `%PLAYER%` tried to join during maintenance."
|
||||
webhookSingleMaintenanceActivated: "Maintenance mode enabled on `%SERVER%`."
|
||||
webhookSingleMaintenanceDeactivated: "Maintenance mode disabled on `%SERVER%`."
|
||||
webhookSingleStarttimerStarted: "Maintenance mode will be enabled on `%SERVER%` <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookSingleEndtimerStarted: "Maintenance mode will be disabled on `%SERVER%` <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
#Used for autoupdating the language file, do not change this value.
|
||||
language-version: 5
|
||||
@@ -1,12 +1,16 @@
|
||||
#Maintenance Plugin by kennytv
|
||||
#You can use https://webui.adventure.kyori.net/ to edit and preview the formatted text
|
||||
#For a full list of formats and fancy examples, see https://docs.adventure.kyori.net/minimessage/format.html
|
||||
#You can use https://webui.advntr.dev/ to edit and preview the formatted text
|
||||
#For a full list of formats and fancy examples, see https://docs.papermc.io/adventure/minimessage/format/
|
||||
#If you want to contribute changes to the default messages, you can do so via https://crowdin.com/project/maintenance
|
||||
#Messages containing the placeholder "<prefix>" will have it replaced with the following string
|
||||
prefix: "<dark_gray>[<yellow>Maintenance<dark_gray>] "
|
||||
noPermission: "<red>Du har ikke tilladelse til at udføre denne kommando."
|
||||
kickmessage: "<red>Serveren er i øjeblikket under vedligeholdelse!<br>Prøv igen senere!"
|
||||
maintenanceActivated: "<prefix><gold>Vedligeholdelsestilstand er nu aktiveret."
|
||||
maintenanceDeactivated: "<prefix><gold>Vedligeholdelsestilstand er nu deaktiveret."
|
||||
maintenanceStatusOn: "<prefix><gold>Maintenance mode is currently <green>enabled<gold>."
|
||||
maintenanceStatusOff: "<prefix><gold>Maintenance mode is currently <red>disabled<gold>."
|
||||
maintenanceStatusOnWithEndTimer: "<prefix><gold>Maintenance mode is currently <green>enabled <grey>and will be disabled in <gold>%TIME%."
|
||||
maintenanceStatusOffWithStartTimer: "<prefix><gold>Maintenance mode is currently <red>disabled <grey>and will be enabled in <gold>%TIME%."
|
||||
alreadyEnabled: "<prefix><red>Vedligeholdelsestilstand er allerede aktiveret!"
|
||||
alreadyDisabled: "<prefix><red>Vedligeholdelsestilstand er allerede deaktiveret!"
|
||||
endtimerBroadcast: "<prefix><gray>Vedligeholdelsestilstand vil blive deaktiveret om <gold>%TIME%<gray>."
|
||||
@@ -29,6 +33,7 @@ reload: "<prefix><green>Genindlæs konfigurationsfilen, hvidlistede spillere, sp
|
||||
removedMotd: "<prefix><green>Slet motd nummer %INDEX%."
|
||||
removeMotdError: "<prefix><red>Du har kun én motd, så du kan ikke slette nogen!"
|
||||
setMotd: "<prefix><green>Angiv linje %LINE% af %INDEX%. vedligeholdelses motd til %MOTD%"
|
||||
setReason: "<prefix><green>Set maintenance reason to: <yellow>%REASON%"
|
||||
setMotdIndexError: "<prefix><red>Du har %MOTDS% motds, så du skal angive et nummer mellem 1 og %NEWAMOUNT%."
|
||||
setMotdLineError: "<prefix><red>Det andet argument skal være linjenummer (1 or 2)!"
|
||||
updateDownloading: "<prefix><red><bold>Downloader opdatering..."
|
||||
@@ -44,7 +49,7 @@ whitelistEmpty: "<prefix><red>Vedligeholdelseshvidlisten er tom! Brug <yellow>/m
|
||||
playerNotFound: "<prefix><red>Ingen spillere med dette navn har spillet på denne server før."
|
||||
playerNotFoundUuid: "<prefix><red>Ingen spiller med den uuid kunne findes."
|
||||
playerNotOnline: "<prefix><red>Der er ingen spiller online med det navn."
|
||||
offlinePlayerFetchError: "<prefix><red>There was an error while fetching offline player. Please try again later."
|
||||
offlinePlayerFetchError: "<prefix><red>Der opstod en fejl under hentning af offline afspiller. Prøv igen senere."
|
||||
invalidUuid: "<prefix><red>Ugyldigt uuid-format!"
|
||||
#Messages for the Bungee/Velocity part, you can ignore them if you use the plugin on Paper/Sponge
|
||||
sentToWaitingServer: "<prefix><red>Du er blevet sendt til en venteserver!"
|
||||
@@ -68,6 +73,8 @@ singleServerAlreadyDisabled: "<prefix><red>Vedligeholdelsestilstand er allerede
|
||||
singleServerMaintenanceList: "<prefix><gray>Proxyservere, der har Vedligeholdelsestilstand aktiveret:"
|
||||
singleServerMaintenanceListEmpty: "<prefix><gray>Der er ingen proxy-servere, der er under vedligeholdelse."
|
||||
singleServerMaintenanceListEntry: "<dark_grey>- <aqua>%SERVER%"
|
||||
singleServerMaintenanceListEntryWithStartTimer: "<dark_grey>- <grey>%SERVER% <yellow>starting in <gold>%TIME%"
|
||||
singleServerMaintenanceListEntryWithEndTimer: "<dark_grey>- <aqua>%SERVER% <yellow>ending in <gold>%TIME%"
|
||||
second: "sekund"
|
||||
seconds: "sekunder"
|
||||
minute: "minut"
|
||||
@@ -87,18 +94,31 @@ helpHelp: "<gold>/maintenance help [page] <gray>(Viser dette smukke hjælpevindu
|
||||
helpMotd: "<gold>/maintenance motd [timer] <gray>(Viser den aktuelle vedligeholdelses motds. Hvis 'timer' er angivet, bliver dennes motds vist)"
|
||||
helpRemoveMotd: "<gold>/maintenance removemotd [timer] <index> <gray>(Sletter en vedligeholdelses motd. Hvis 'timer' er angivet, vil en tidsspecifik pingbesked blive slettet)"
|
||||
helpSetMotd: "<gold>/maintenance setmotd [timer] <index> <1/2> <message> <gray>(Angiver en motd for vedligeholdelsestilstand. Hvis 'timer' er angivet, vil en tidsspecifik pingbesked blive angivet)"
|
||||
helpSetReason: "<gold>/maintenance setreason <reason/none> <gray>(Sets or clears the %REASON% placeholder value)"
|
||||
helpReload: "<gold>/maintenance reload <gray>(Genindlæser konfigurationsfilen, hvidlisten og serverikonet)"
|
||||
helpToggle: "<gold>/maintenance <on/off> <gray>(Aktiverer/deaktiverer vedligeholdelsestilstand)"
|
||||
helpGlobalStatus: "<gold>/maintenance status <gray>(Shows the current maintenance status and any running timers)"
|
||||
helpToggle: "<gold>/maintenance <on/off> [mode] <gray>(Enables/disables maintenance mode with an optional display mode when enabling)"
|
||||
helpUpdate: "<gold>/maintenance update <gray>(Downloader den nyeste version af plugin'et på din server)"
|
||||
helpWhitelist: "<gold>/maintenance whitelist <gray>(Viser alle hvidlistede spillere for vedligeholdelsestilstand)"
|
||||
helpWhitelistAdd: "<gold>/maintenance add <name/uuid> <gray>(Tilføjer spilleren til vedligeholdelseshvidlisten, så de kan forbinde til serveren, selvom vedligeholdelsestilstand er aktiveret)"
|
||||
helpWhitelistRemove: "<gold>/maintenance remove <name/uuid> <gray>(Sletter spilleren fra vedligeholdelseshvidlisten)"
|
||||
#Messages for the Bungee/Velocity part, you can ignore them if you use the plugin on Paper/Sponge
|
||||
helpAbortSingleTimer: "<gold>/maintenance aborttimer [server] <gray>(Hvis timeren kører, vil den blive afbrudt)"
|
||||
helpSingleEndtimer: "<gold>/maintenance endtimer [server] <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpSingleStarttimer: "<gold>/maintenance starttimer [server] <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleScheduleTimer: "<gold>/maintenance scheduletimer [server] <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleToggle: "<gold>/maintenance <on/off> [server] <gray>(Aktiverer/deaktiverer vedligeholdelsestilstand)"
|
||||
helpSingleEndtimer: "<gold>/maintenance endtimer [server|global] <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpSingleStarttimer: "<gold>/maintenance starttimer [server|global] [mode] <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleScheduleTimer: "<gold>/maintenance scheduletimer [server|global] [mode] <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleToggle: "<gold>/maintenance <on/off> [server|global] [mode] <gray>(Enables/disables maintenance mode globally or on a specific server with an optional display mode when enabling)"
|
||||
helpStatus: "<gold>/maintenance status <gray>(Viser alle proxyservere, der har Vedligeholdelsestilstand aktiveret)"
|
||||
#Discord webhook messages. These use Discord markdown, not MiniMessage.
|
||||
#For timer webhooks, %TIMESTAMP% is a Unix timestamp for use in Discord timestamp tags, e.g. <t:%TIMESTAMP%:R>.
|
||||
webhookMaintenanceActivated: "**Maintenance mode enabled.**"
|
||||
webhookMaintenanceDeactivated: "**Maintenance mode disabled.**"
|
||||
webhookStarttimerStarted: "Maintenance mode will be enabled <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookEndtimerStarted: "Maintenance mode will be disabled <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookJoinNotification: "Blocked join: `%PLAYER%` tried to join during maintenance."
|
||||
webhookSingleMaintenanceActivated: "Maintenance mode enabled on `%SERVER%`."
|
||||
webhookSingleMaintenanceDeactivated: "Maintenance mode disabled on `%SERVER%`."
|
||||
webhookSingleStarttimerStarted: "Maintenance mode will be enabled on `%SERVER%` <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookSingleEndtimerStarted: "Maintenance mode will be disabled on `%SERVER%` <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
#Used for autoupdating the language file, do not change this value.
|
||||
language-version: 2
|
||||
language-version: 5
|
||||
@@ -1,12 +1,16 @@
|
||||
#Maintenance Plugin by kennytv
|
||||
#You can use https://webui.adventure.kyori.net/ to edit and preview the formatted text
|
||||
#For a full list of formats and fancy examples, see https://docs.adventure.kyori.net/minimessage/format.html
|
||||
#You can use https://webui.advntr.dev/ to edit and preview the formatted text
|
||||
#For a full list of formats and fancy examples, see https://docs.papermc.io/adventure/minimessage/format/
|
||||
#If you want to contribute changes to the default messages, you can do so via https://crowdin.com/project/maintenance
|
||||
#Messages containing the placeholder "<prefix>" will have it replaced with the following string
|
||||
prefix: "<dark_gray>[<yellow>Maintenance<dark_gray>] "
|
||||
noPermission: "<red>Du hast nicht die erforderliche Berechtigung, um diesen Befehl auszuführen."
|
||||
kickmessage: "<red>Der Server befindet sich momentan im Wartungsmodus!<br>Bitte versuche es später nochmal!"
|
||||
maintenanceActivated: "<prefix><gold>Der Wartungsmodus ist jetzt aktiviert."
|
||||
maintenanceDeactivated: "<prefix><gold>Der Wartungsmodus ist nun deaktiviert."
|
||||
maintenanceStatusOn: "<prefix><gold>Wartungsmodus ist derzeit <green>aktiviert<gold>."
|
||||
maintenanceStatusOff: "<prefix><gold>Wartungsmodus ist momentan <red>deaktiviert<gold>."
|
||||
maintenanceStatusOnWithEndTimer: "<prefix><gold>Wartungsmodus ist momentan <green>aktiviert <grey>und wird deaktiviert in <gold>%TIME%."
|
||||
maintenanceStatusOffWithStartTimer: "<prefix><gold>Wartungsmodus ist momentan <red>deaktiviert <grey>und wird aktiviert in <gold>%TIME%."
|
||||
alreadyEnabled: "<prefix><red>Der Wartungsmodus ist bereits aktiviert!"
|
||||
alreadyDisabled: "<prefix><red>Der Wartungsmodus ist bereits deaktiviert!"
|
||||
endtimerBroadcast: "<prefix><gray>Der Wartungsmodus wird deaktiviert in: <gold>%TIME%<gray>."
|
||||
@@ -26,9 +30,10 @@ joinNotification: "<prefix><yellow>%PLAYER% <red>hat versucht dem Server beizutr
|
||||
motdList: "<prefix><gray>Liste deiner Motds, die während Maintenance angezeigt werden:"
|
||||
motdListEmpty: "<prefix><red>Du hast keine Maintenance MOTDs gesetzt!"
|
||||
reload: "<prefix><green>Die Config, die Liste der Spieler die während Maintenance beitreten können, die Sprachdateien und das Icon, das während der Maintenance angezeigt wird, wurden reloaded."
|
||||
removedMotd: "<prefix><green>Die %INDEX%. Wartungs-MOTD wurde entfernt."
|
||||
removedMotd: "<prefix><green>Die %INDEX%. Maintenance MOTD wurde entfernt."
|
||||
removeMotdError: "<prefix><red>Du hast nur eine Maintenance MOTD eingestellt, daher kannst du auch keine entfernen!"
|
||||
setMotd: "<prefix><green>Die %LINE%. Zeile der %INDEX%. Maintenance MOTD wurde geändert zu: %MOTD%"
|
||||
setReason: "<prefix><green>Grund für die Wartung gesetzt auf: <yellow>%REASON%"
|
||||
setMotdIndexError: "<prefix><red>Momentan gibt es %MOTDS% MOTDs, du musst also eine Zahl, die zwischen 1 und %NEWAMOUNT% liegt, wählen."
|
||||
setMotdLineError: "<prefix><red>Das zweite Argument des Befehls muss die Zeilennummer sein (1 oder 2)!"
|
||||
updateDownloading: "<prefix><red><bold>Das Update wird heruntergeladen..."
|
||||
@@ -68,6 +73,8 @@ singleServerAlreadyDisabled: "<prefix><red>Der Wartungsmodus ist bereits aus auf
|
||||
singleServerMaintenanceList: "<prefix><gray>Unterserver, die im Wartungsmodus sind:"
|
||||
singleServerMaintenanceListEmpty: "<prefix><gray>Es befindet sich momentan kein Unterserver im Wartungsmodus."
|
||||
singleServerMaintenanceListEntry: "<dark_grey>- <aqua>%SERVER%"
|
||||
singleServerMaintenanceListEntryWithStartTimer: "<dark_grey>- <grey>%SERVER% <yellow>startet in <gold>%TIME%"
|
||||
singleServerMaintenanceListEntryWithEndTimer: "<dark_grey>- <aqua>%SERVER% <yellow>endet in <gold>%TIME%"
|
||||
second: "Sekunde"
|
||||
seconds: "Sekunden"
|
||||
minute: "Minute"
|
||||
@@ -84,21 +91,34 @@ helpScheduleTimer: "<gold>/maintenance scheduletimer <Aktiviere in> <Wartungsdau
|
||||
helpDebug: "<gold>/maintenance debug <gray>(Aktiviert Debug Nachrichten in der Konsole)"
|
||||
helpDump: "<gold>/maintenance dump <gray>(Sammelt wichtige Informationen für Bugreports)"
|
||||
helpHelp: "<gold>/maintenance help [Seite] <gray>(Zeigt diese wunderschöne Hilfe)"
|
||||
helpMotd: "<gold>/maintenance motd [Timer] <gray>(Zeigt die momentan gesetzten Maintenance Motds an. Mit dem 'timer' Argument werden die timerspezifischen Motds angezeigt)"
|
||||
helpRemoveMotd: "<gold>/maintenance removemotd [Timer] <Index> <gray>(Entfernt eine Maintenance Motd. Mit dem 'timer' Argument wird eine timerspezifischen Motd entfernt)"
|
||||
helpSetMotd: "<gold>/maintenance setmotd [Timer] <Index> <1/2> <Nachricht> <gray>(Setzte eine Motd für den Wartungsmodus. Mit dem 'timer' Argument wird eine timerspezifische Motd gesetzt)"
|
||||
helpMotd: "<gold>/maintenance motd [Timer] [Modus] <gray>(Zeigt die momentan gesetzten Maintenance Motds an. Mit dem 'timer' Argument werden die timerspezifischen Motds angezeigt)"
|
||||
helpRemoveMotd: "<gold>/maintenance removemotd [Timer] [Modus] <Index> <gray>(Entfernt eine Maintenance Motd. Mit dem 'timer' Argument wird eine timerspezifischen Motd entfernt)"
|
||||
helpSetMotd: "<gold>/maintenance setmotd [Timer] [Modus] <Index> <1/2> <Nachricht> <gray>(Setzte eine Motd für den Wartungsmodus. Mit dem 'timer' Argument wird eine timerspezifische Motd gesetzt)"
|
||||
helpSetReason: "<gold>/maintenance setreason <reason/none> <gray>(Setzt oder löscht den %REASON% Platzhalter)"
|
||||
helpReload: "<gold>/maintenance reload <gray>(Lädt Config, Language und Whitelist Dateien sowie das Maintenance Icon neu)"
|
||||
helpToggle: "<gold>/maintenance <on/off> <gray>(Aktiviert/deaktiviert den Wartungsmodus)"
|
||||
helpGlobalStatus: "<gold>/maintenance status <gray>(Zeigt den aktuellen Wartungsstatus und alle laufenden Timer an)"
|
||||
helpToggle: "<gold>/maintenance <on/off> [mode] <gray>(Aktiviert/deaktiviert den Wartungsmodus mit einem optionalen Anzeigemodus beim Aktivieren)"
|
||||
helpUpdate: "<gold>/maintenance update <gray>(Sucht nach neuen Updates für das Plugin und lädt diese auf den Server)"
|
||||
helpWhitelist: "<gold>/maintenance whitelist <gray>(Zeigt alle gewhitelisteten Spieler für den Wartungsmodus an)"
|
||||
helpWhitelistAdd: "<gold>/maintenance add <Name/UUID> <gray>(Whitelistet einen Spieler für den Wartungsmodus, sodass dieser während Wartungen den Server betreten kann)"
|
||||
helpWhitelistRemove: "<gold>/maintenance remove <Name/UUID> <gray>(Entfernt einen Spieler aus der Wartungsmodus Whitelist)"
|
||||
#Messages for the Bungee/Velocity part, you can ignore them if you use the plugin on Paper/Sponge
|
||||
helpAbortSingleTimer: "<gold>/maintenance aborttimer [Server] <gray>(Falls momentan ein Timer läuft, wird dieser abgebrochen)"
|
||||
helpSingleEndtimer: "<gold>/maintenance endtimer [Server] <Deaktiviere in> <gray>(Sobald der Timer, bspw. 1h5m, abläuft wird der Wartungsmodus deaktiviert)"
|
||||
helpSingleStarttimer: "<gold>/maintenance starttimer [Server] <Aktiviere in> <gray>(Sobald der Timer, bspw. 1h5m, abläuft wird der Wartungsmodus aktiviert)"
|
||||
helpSingleScheduleTimer: "<gold>/maintenance scheduletimer [Server] <Starte in> <Wartungsdauer> <gray>(Sobald der Timer, bspw. 1h5m, abläuft wird der Wartungsmodus für die gegebene Länge aktiviert)"
|
||||
helpSingleToggle: "<gold>/maintenance <on/off> [Server] <gray>(Aktiviert/deaktiviert den Wartungsmodus)"
|
||||
helpSingleEndtimer: "<gold>/maintenance endtimer [server|global] <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpSingleStarttimer: "<gold>/maintenance starttimer [server|global] [mode] <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleScheduleTimer: "<gold>/maintenance scheduletimer [server|global] [mode] <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleToggle: "<gold>/maintenance <on/off> [server|global] [mode] <gray>(Aktiviert/deaktiviert den Wartungsmodus global oder auf einem bestimmten Server mit optionalem Anzeigemodus beim Aktivieren)"
|
||||
helpStatus: "<gold>/maintenance status <gray>(Zeigt alle Server an, auf denen momentan der Wartungsmodus aktiviert ist)"
|
||||
#Discord webhook messages. These use Discord markdown, not MiniMessage.
|
||||
#For timer webhooks, %TIMESTAMP% is a Unix timestamp for use in Discord timestamp tags, e.g. <t:%TIMESTAMP%:R>.
|
||||
webhookMaintenanceActivated: "**Wartungsmodus aktiviert.**"
|
||||
webhookMaintenanceDeactivated: "**Wartungsmodus deaktiviert.**"
|
||||
webhookStarttimerStarted: "Der Wartungsmodus wird aktiviert in <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookEndtimerStarted: "Der Wartungsmodus wird deaktiviert in <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookJoinNotification: "Beitritt gesperrt: `%PLAYER%` hat versucht, während den Wartungsarbeiten beizutreten."
|
||||
webhookSingleMaintenanceActivated: "Der Wartungsmodus wurde aktiviert auf `%SERVER%`."
|
||||
webhookSingleMaintenanceDeactivated: "Der Wartungsmodus wurde deaktiviert auf `%SERVER%`."
|
||||
webhookSingleStarttimerStarted: "Der Wartungsmodus wird aktiviert auf `%SERVER%` <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookSingleEndtimerStarted: "Der Wartungsmodus wird deaktiviert auf `%SERVER%` <t:%TIMESTAMP%:R>(`%TIME%`)."
|
||||
#Used for autoupdating the language file, do not change this value.
|
||||
language-version: 2
|
||||
language-version: 4
|
||||
@@ -1,12 +1,16 @@
|
||||
#Maintenance Plugin by kennytv
|
||||
#You can use https://webui.adventure.kyori.net/ to edit and preview the formatted text
|
||||
#For a full list of formats and fancy examples, see https://docs.adventure.kyori.net/minimessage/format.html
|
||||
#You can use https://webui.advntr.dev/ to edit and preview the formatted text
|
||||
#For a full list of formats and fancy examples, see https://docs.papermc.io/adventure/minimessage/format/
|
||||
#If you want to contribute changes to the default messages, you can do so via https://crowdin.com/project/maintenance
|
||||
#Messages containing the placeholder "<prefix>" will have it replaced with the following string
|
||||
prefix: "<dark_gray>[<yellow>Maintenance<dark_gray>] "
|
||||
noPermission: "<red>You do not have the permission to execute that command."
|
||||
kickmessage: "<red>The server is currently under maintenance!<br>Try again later!"
|
||||
maintenanceActivated: "<prefix><gold>Maintenance mode is now activated."
|
||||
maintenanceDeactivated: "<prefix><gold>Maintenance mode is now deactivated."
|
||||
maintenanceStatusOn: "<prefix><gold>Maintenance mode is currently <green>enabled<gold>."
|
||||
maintenanceStatusOff: "<prefix><gold>Maintenance mode is currently <red>disabled<gold>."
|
||||
maintenanceStatusOnWithEndTimer: "<prefix><gold>Maintenance mode is currently <green>enabled <grey>and will be disabled in <gold>%TIME%."
|
||||
maintenanceStatusOffWithStartTimer: "<prefix><gold>Maintenance mode is currently <red>disabled <grey>and will be enabled in <gold>%TIME%."
|
||||
alreadyEnabled: "<prefix><red>Maintenance is already enabled!"
|
||||
alreadyDisabled: "<prefix><red>Maintenance is already disabled!"
|
||||
endtimerBroadcast: "<prefix><gray>Maintenance mode will be disabled in <gold>%TIME%<gray>."
|
||||
@@ -29,6 +33,7 @@ reload: "<prefix><green>Reloaded config, whitelistedplayers, language file and t
|
||||
removedMotd: "<prefix><green>Removed motd number %INDEX%."
|
||||
removeMotdError: "<prefix><red>You only have one motd, so you cannot remove any!"
|
||||
setMotd: "<prefix><green>Set line %LINE% of the %INDEX%. maintenance motd to %MOTD%"
|
||||
setReason: "<prefix><green>Set maintenance reason to: <yellow>%REASON%"
|
||||
setMotdIndexError: "<prefix><red>You currently have %MOTDS% motds, so you have to pick a number between 1 and %NEWAMOUNT%."
|
||||
setMotdLineError: "<prefix><red>The second argument has to be the line number (1 or 2)!"
|
||||
updateDownloading: "<prefix><red><bold>Downloading update..."
|
||||
@@ -68,6 +73,8 @@ singleServerAlreadyDisabled: "<prefix><red>Maintenance is already disabled on se
|
||||
singleServerMaintenanceList: "<prefix><gray>Proxied servers, that have maintenance enabled:"
|
||||
singleServerMaintenanceListEmpty: "<prefix><gray>There are no proxied servers that are under maintenance."
|
||||
singleServerMaintenanceListEntry: "<dark_grey>- <aqua>%SERVER%"
|
||||
singleServerMaintenanceListEntryWithStartTimer: "<dark_grey>- <grey>%SERVER% <yellow>starting in <gold>%TIME%"
|
||||
singleServerMaintenanceListEntryWithEndTimer: "<dark_grey>- <aqua>%SERVER% <yellow>ending in <gold>%TIME%"
|
||||
second: "second"
|
||||
seconds: "seconds"
|
||||
minute: "minute"
|
||||
@@ -84,21 +91,34 @@ helpScheduleTimer: "<gold>/maintenance scheduletimer <enable in> <maintenance du
|
||||
helpDebug: "<gold>/maintenance debug <gray>(Enables some debug logging)"
|
||||
helpDump: "<gold>/maintenance dump <gray>(Dumps some server information, used for bug reports)"
|
||||
helpHelp: "<gold>/maintenance help [page] <gray>(Shows this beautiful help window)"
|
||||
helpMotd: "<gold>/maintenance motd [timer] <gray>(Lists the currently set maintenance motds. If specifying 'timer', the timer motds are shown)"
|
||||
helpRemoveMotd: "<gold>/maintenance removemotd [timer] <index> <gray>(Removes a maintenance motd. If using 'timer' as an argument, a timerspecific pingmessage will be removed)"
|
||||
helpSetMotd: "<gold>/maintenance setmotd [timer] <index> <1/2> <message> <gray>(Sets a motd for maintenance mode. If using 'timer' as an argument, a timerspecific pingmessage will be set)"
|
||||
helpMotd: "<gold>/maintenance motd [timer] [mode] <gray>(Lists maintenance motds. If 'timer' is used, timer-specific motds are shown)"
|
||||
helpRemoveMotd: "<gold>/maintenance removemotd [timer] [mode] <index> <gray>(Removes a maintenance motd in the selected mode)"
|
||||
helpSetMotd: "<gold>/maintenance setmotd [timer] [mode] <index> <1/2> <message> <gray>(Sets a maintenance motd in the selected mode)"
|
||||
helpSetReason: "<gold>/maintenance setreason <reason/none> <gray>(Sets or clears the %REASON% placeholder value)"
|
||||
helpReload: "<gold>/maintenance reload <gray>(Reloads the config file, whitelist file and the server-icon)"
|
||||
helpToggle: "<gold>/maintenance <on/off> <gray>(Enables/disables maintenance mode)"
|
||||
helpGlobalStatus: "<gold>/maintenance status <gray>(Shows the current maintenance status and any running timers)"
|
||||
helpToggle: "<gold>/maintenance <on/off> [mode] <gray>(Enables/disables maintenance mode with an optional display mode when enabling)"
|
||||
helpUpdate: "<gold>/maintenance update <gray>(Remotely downloads the newest version of the plugin onto your server)"
|
||||
helpWhitelist: "<gold>/maintenance whitelist <gray>(Shows all whitelisted players for the maintenance mode)"
|
||||
helpWhitelistAdd: "<gold>/maintenance add <name/uuid> <gray>(Adds the player to the maintenance whitelist, so they can join the server even though maintenance is enabled)"
|
||||
helpWhitelistRemove: "<gold>/maintenance remove <name/uuid> <gray>(Removes the player from the maintenance whitelist)"
|
||||
#Messages for the Bungee/Velocity part, you can ignore them if you use the plugin on Paper/Sponge
|
||||
helpAbortSingleTimer: "<gold>/maintenance aborttimer [server] <gray>(If running, the current timer will be aborted)"
|
||||
helpSingleEndtimer: "<gold>/maintenance endtimer [server] <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpSingleStarttimer: "<gold>/maintenance starttimer [server] <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleScheduleTimer: "<gold>/maintenance scheduletimer [server] <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleToggle: "<gold>/maintenance <on/off> [server] <gray>(Enables/disables maintenance mode)"
|
||||
helpSingleEndtimer: "<gold>/maintenance endtimer [server|global] <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpSingleStarttimer: "<gold>/maintenance starttimer [server|global] [mode] <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleScheduleTimer: "<gold>/maintenance scheduletimer [server|global] [mode] <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleToggle: "<gold>/maintenance <on/off> [server|global] [mode] <gray>(Enables/disables maintenance mode globally or on a specific server with an optional display mode when enabling)"
|
||||
helpStatus: "<gold>/maintenance status <gray>(Lists all proxied servers, that are currently under maintenance)"
|
||||
#Discord webhook messages. These use Discord markdown, not MiniMessage.
|
||||
#For timer webhooks, %TIMESTAMP% is a Unix timestamp for use in Discord timestamp tags, e.g. <t:%TIMESTAMP%:R>.
|
||||
webhookMaintenanceActivated: "**Maintenance mode enabled.**"
|
||||
webhookMaintenanceDeactivated: "**Maintenance mode disabled.**"
|
||||
webhookStarttimerStarted: "Maintenance mode will be enabled <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookEndtimerStarted: "Maintenance mode will be disabled <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookJoinNotification: "Blocked join: `%PLAYER%` tried to join during maintenance."
|
||||
webhookSingleMaintenanceActivated: "Maintenance mode enabled on `%SERVER%`."
|
||||
webhookSingleMaintenanceDeactivated: "Maintenance mode disabled on `%SERVER%`."
|
||||
webhookSingleStarttimerStarted: "Maintenance mode will be enabled on `%SERVER%` <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookSingleEndtimerStarted: "Maintenance mode will be disabled on `%SERVER%` <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
#Used for autoupdating the language file, do not change this value.
|
||||
language-version: 2
|
||||
language-version: 5
|
||||
@@ -1,39 +1,44 @@
|
||||
#Maintenance Plugin by kennytv
|
||||
#You can use https://webui.adventure.kyori.net/ to edit and preview the formatted text
|
||||
#For a full list of formats and fancy examples, see https://docs.adventure.kyori.net/minimessage/format.html
|
||||
#You can use https://webui.advntr.dev/ to edit and preview the formatted text
|
||||
#For a full list of formats and fancy examples, see https://docs.papermc.io/adventure/minimessage/format/
|
||||
#If you want to contribute changes to the default messages, you can do so via https://crowdin.com/project/maintenance
|
||||
#Messages containing the placeholder "<prefix>" will have it replaced with the following string
|
||||
prefix: "<dark_gray>[<yellow>Maintenance<dark_gray>] "
|
||||
noPermission: "<prefix><dark_red>No tienes permiso para ejecutar este comando."
|
||||
kickmessage: "<red>¡El servidor se encuentra en mantenimiento!<br>¡Intentalo de nuevo más tarde!"
|
||||
maintenanceActivated: "<prefix><gold>Mantenimiento <gray>activado."
|
||||
maintenanceDeactivated: "<prefix><gold>Modo mantenimiento desactivado."
|
||||
alreadyEnabled: "<prefix><gray>¡Modo mantenimiento se encuentra habilitado!"
|
||||
alreadyDisabled: "<prefix><gray>¡Modo mantenimiento se encuentra deshabilitado!"
|
||||
maintenanceActivated: "<prefix><gold>Modo Mantenimiento activado."
|
||||
maintenanceDeactivated: "<prefix><gold>Modo Mantenimiento desactivado."
|
||||
maintenanceStatusOn: "<prefix><gold>El modo de mantenimiento está actualmente <green>activado<gold>."
|
||||
maintenanceStatusOff: "<prefix><gold>El modo de mantenimiento está actualmente <red>desactivado<gold>."
|
||||
maintenanceStatusOnWithEndTimer: "<prefix><gold>El modo de mantenimiento está actualmente <green>habilitado <grey>y se desactivará en <gold>%TIME%."
|
||||
maintenanceStatusOffWithStartTimer: "<prefix><gold>El modo de mantenimiento está actualmente <red>desactivado <grey>y se activará en <gold>%TIME%."
|
||||
alreadyEnabled: "<prefix><red>¡Modo mantenimiento ya está activado!"
|
||||
alreadyDisabled: "<prefix><red>¡Modo mantenimiento ya está desactivado!"
|
||||
endtimerBroadcast: "<prefix><gray>El modo mantenimiento se desactivará en <gold>%TIME%<gray>."
|
||||
endtimerStarted: "<prefix><gray>Temporizador iniciado: El modo mantenimiento se desactivará en <gold>%TIME%<gray>."
|
||||
starttimerBroadcast: "<prefix><gray>El modo mantenimiento se habilitara en <gold>%TIME%<gray>."
|
||||
starttimerStarted: "<prefix><gray>Temporizador iniciado: El modo mantenimiento se activará en <gold>%TIME%<gray>."
|
||||
scheduletimerBroadcast: "<prefix><gray>Maintenance mode will be enabled in <gold>%TIME% <gray>and will last for <gold>%DURATION%<gray>."
|
||||
endtimerStarted: "<prefix><green>Temporizador iniciado: <gray>El modo mantenimiento se desactivará en <gold>%TIME%<gray>."
|
||||
starttimerBroadcast: "<prefix><gray>El modo mantenimiento se activará en <gold>%TIME%<gray>."
|
||||
starttimerStarted: "<prefix><green>Temporizador iniciado: <gray>El modo mantenimiento se activará en <gold>%TIME%<gray>."
|
||||
scheduletimerBroadcast: "<prefix><gray>Temporizador iniciado: El modo mantenimiento se activará en <gold>%TIME% <gray>y durará <gold>%DURATION%<gray>."
|
||||
scheduletimerStarted: "<prefix><gray>Temporizador iniciado: El modo mantenimiento se activará en <gold>%TIME% <gray>y durará <gold>%DURATION%<gray>."
|
||||
timerAlreadyRunning: "<prefix><red>Ya hay un temporizador programado!"
|
||||
timerNotRunning: "<prefix><gray>Actualmente no hay temporizador."
|
||||
timerAlreadyRunning: "<prefix><red>¡Ya hay un temporizador programado!"
|
||||
timerNotRunning: "<prefix><red>Actualmente no hay ningún temporizador."
|
||||
timerCancelled: "<prefix><red>El Temporizador actual ha sido deshabilitado."
|
||||
timerTooLong: "<prefix><gray>El numero debe ser menor a 40320 <dark_gray>(<gold>28 días<dark_gray>)"
|
||||
timerMotdDisabled: "<prefix><red>Tienes que configurar <green>'enable-timerspecific-messages'<red> en la configuración en <green>'true'<red>, si quieres usar / editar motdspecers específicos."
|
||||
timerTooLong: "<prefix><red>¡La duración debe ser menor a 28 días!"
|
||||
timerMotdDisabled: "<prefix><red>Debes configurar 'enable-timerspecific-messages' en la configuración a 'true', si quieres usar/editar motds por temporizador."
|
||||
motdTimer: "%HOURS%:%MINUTES%:%SECONDS%"
|
||||
motdTimerNotRunning: "-"
|
||||
joinNotification: "<prefix><red>%PLAYER% <gray>Intenta unirse al servidor."
|
||||
joinNotification: "<prefix><yellow>%PLAYER% <red>intentó unirse al servidor."
|
||||
motdList: "<prefix><gray>Lista de motds de mantenimiento:"
|
||||
motdListEmpty: "<prefix><red>¡No tiene ningún motd(s<dark_gray>) de mantenimiento establecido!"
|
||||
motdListEmpty: "<prefix><red>¡No tienes ningún motd(s) de mantenimiento establecido!"
|
||||
reload: "<prefix><green>Configuracion recargada"
|
||||
removedMotd: "<prefix><green>Numero de motd eliminado <gold>%INDEX%<green>."
|
||||
removeMotdError: "<prefix>&Solo tienes un motd, por lo que no puedes eliminar ninguno!"
|
||||
setMotd: "<prefix><green>Establecer linea <gold>%LINE%<green> sobre <gold>%INDEX%<green>. Para mantenimiento para %MOTD%"
|
||||
setReason: "<prefix><green>Se estableció el motivo de mantenimiento a: <yellow>%REASON%"
|
||||
setMotdIndexError: "<prefix><green>Actualmente tiene <gold>%MOTDS%<green> Motd(s<dark_gray>), por lo que debe elegir un número entre 1 y %NEWAMOUNT%."
|
||||
setMotdLineError: "<prefix><gray>El segundo argumento tiene que ser el numero de línea.(1 ó 2<dark_gray>)!"
|
||||
updateDownloading: "<prefix><green>Descargando actualización..."
|
||||
updateFailed: "<prefix><red>Actualizacion fallida!"
|
||||
updateFinished: "<prefix><green>La actualización fue exitosa!, debes reiniciar el servidor!"
|
||||
updateFinished: "<prefix><green><bold>The update was successful! To prevent issues with tasks and to complete the update, you have to restart the server!"
|
||||
whitelistedPlayers: "<prefix><gold>Jugadores en lista blanca:"
|
||||
whitelistedPlayersFormat: "<gray>- <green>%NAME% <dark_gray>(<gray>%UUID%<dark_gray>)"
|
||||
whitelistAdded: "<prefix><green>%PLAYER% <gray>Añadido a la lista de mantenimiento!"
|
||||
@@ -68,6 +73,8 @@ singleServerAlreadyDisabled: "<prefix><gray>El modo mantenimiento se encuentra d
|
||||
singleServerMaintenanceList: "<prefix><gray>Servidores proxy, que tienen habilitado el mantenimiento:"
|
||||
singleServerMaintenanceListEmpty: "<prefix><gray>No hay servidores proxy que están en mantenimiento."
|
||||
singleServerMaintenanceListEntry: "<dark_grey>- <aqua>%SERVER%"
|
||||
singleServerMaintenanceListEntryWithStartTimer: "<dark_grey>- <yellow>Abriendo <grey>%SERVER% <yellow>en <gold>%TIME%"
|
||||
singleServerMaintenanceListEntryWithEndTimer: "<dark_grey>- <yellow>Cerrando <aqua>%SERVER% <yellow>en <gold>%TIME%"
|
||||
second: "Segundo"
|
||||
seconds: "Segundos"
|
||||
minute: "Minuto"
|
||||
@@ -78,27 +85,40 @@ helpHeader: "<dark_gray>========[ <yellow>%NAME% <dark_gray>| <yellow>%PAGE%/%MA
|
||||
helpPageNotFound: "<prefix><gray>No hay pagina con ese número."
|
||||
helpNextPage: "<gray>Utilice <aqua>/maintenance help %PAGE% <gray>Para pasar a la siguiente ventana de ayuda."
|
||||
helpAbortTimer: "<gold>/maintenance aborttimer <dark_gray>(<gray>Si se ejecuta, se abortará el temporizador actual<dark_gray>)"
|
||||
helpEndtimer: "<gold>/maintenance endtimer <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpStarttimer: "<gold>/maintenance starttimer <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpScheduleTimer: "<gold>/maintenance scheduletimer <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpEndtimer: "<gold>/maintenance endtimer <disable in> <gray> (Después del tiempo establecido, ej. 1h5m, el modo de mantenimiento será desactivado)"
|
||||
helpStarttimer: "<gold>/maintenance starttimer <enable in> <gray>(Después del tiempo indicado, ej: 1h5m, El modo mantenimiento se habilita.)"
|
||||
helpScheduleTimer: "<gold>/maintenance scheduletimer <enable in> <maintenance duration> <gray>(Después del tiempo indicado, ej. 1h5m, el modo mantenimiento se habilitará.)"
|
||||
helpDebug: "<gold>/maintenance debug <gray>(Activa algunos registros de depuración)"
|
||||
helpDump: "<gold>/maintenance dump <dark_gray>(<gray>Descarga información del servidor, usada para informes de errores<dark_gray>)"
|
||||
helpHelp: "<gold>/maintenance help [page] <dark_gray>(<gray>Muestra la pagina de ayuda<dark_gray>)"
|
||||
helpMotd: "<gold>/maintenance motd [timer] <dark_gray>(<gray>Enumera los motivos de mantenimiento establecidos actualmente. Si se especifica el 'temporizador', se muestran los motivos del temporizador<dark_gray>)"
|
||||
helpRemoveMotd: "<gold>/maintenance removemotd [timer] <index> <dark_gray>(<gray>Elimina un mantenimiento motd. Si se usa 'timer' como argumento, se eliminará un pingmessage de temporizador específico<dark_gray>)"
|
||||
helpSetMotd: "<gold>/maintenance setmotd [timer] <index> <1/2> <message> <dark_gray>(<gray>Establece un motd para el modo de mantenimiento. Si usa 'timer' como argumento, se establecerá un mensaje de ping específico para el tiempo<dark_gray>)"
|
||||
helpSetReason: "<gold>/maintenance setreason <reason/none> <gray>(Configura o reestablece el valor por defecto de %REASON%)"
|
||||
helpReload: "<gold>/maintenance reload <dark_gray>(<gray>Vuelve a cargar el archivo de configuración, el archivo de la lista blanca y el ícono del servidor<dark_gray>)"
|
||||
helpToggle: "<gold>/maintenance <on/off> <dark_gray>(<gray>Habilita / deshabilita el modo de mantenimiento<dark_gray>)"
|
||||
helpGlobalStatus: "<gold>/maintenance status <gray>(Muestra el estado de mantenimiento actual y cualquier temporizador activo)"
|
||||
helpToggle: "<gold>/maintenance <on/off> [mode] <gray>(Habilita/desactiva el modo de mantenimiento con un modo de visualización opcional al habilitar)"
|
||||
helpUpdate: "<gold>/maintenance update <dark_gray>(<gray>Descarga de forma remota la versión más reciente del complemento en su servidor<dark_gray>)"
|
||||
helpWhitelist: "<gold>/maintenance whitelist <dark_gray>(<gray>Muestra todos los jugadores en lista blanca para el modo de mantenimiento<dark_gray>)"
|
||||
helpWhitelistAdd: "<gold>/maintenance add <name/uuid> <dark_gray>(<gray>Agrega al jugador a la lista blanca de mantenimiento, para que puedan unirse al servidor aunque el mantenimiento esté habilitado<dark_gray>)"
|
||||
helpWhitelistRemove: "<gold>/maintenance remove <name/uuid> <dark_gray>(<gray>Quita al jugador de la lista blanca de mantenimiento<dark_gray>)"
|
||||
#Messages for the Bungee/Velocity part, you can ignore them if you use the plugin on Paper/Sponge
|
||||
helpAbortSingleTimer: "<gold>/maintenance aborttimer [servidor] <dark_gray>(<gray>Si está funcionando, se abortará el temporizador actual<dark_gray>)"
|
||||
helpSingleEndtimer: "<gold>/maintenance endtimer [server] <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpSingleStarttimer: "<gold>/maintenance starttimer [server] <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleScheduleTimer: "<gold>/maintenance scheduletimer [server] <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleToggle: "<gold>/maintenance <on/off> [server] <dark_gray>(<gray>Habilita / deshabilita el modo de mantenimiento<dark_gray>)"
|
||||
helpSingleEndtimer: "<gold>/maintenance endtimer [server|global] <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpSingleStarttimer: "<gold>/maintenance starttimer [server|global] [mode] <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleScheduleTimer: "<gold>/maintenance scheduletimer [server|global] [mode] <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleToggle: "<gold>/maintenance <on/off> [servidor|global] [mode] <gray> (Habilita/desactiva el modo de mantenimiento globalmente o en un servidor específico con un modo de visualización opcional al habilitar)"
|
||||
helpStatus: "<gold>/maintenance status <dark_gray>(<gray>Enumera todos los servidores proxy, que están actualmente en mantenimiento<dark_gray>)"
|
||||
#Discord webhook messages. These use Discord markdown, not MiniMessage.
|
||||
#For timer webhooks, %TIMESTAMP% is a Unix timestamp for use in Discord timestamp tags, e.g. <t:%TIMESTAMP%:R>.
|
||||
webhookMaintenanceActivated: "**Modo de mantenimiento activado.**"
|
||||
webhookMaintenanceDeactivated: "**Modo de mantenimiento desactivado.**"
|
||||
webhookStarttimerStarted: "<prefix><gray>El modo mantenimiento se activará en <gold>%TIME%<gray>`)."
|
||||
webhookEndtimerStarted: "<prefix><gray>El modo mantenimiento se desactivará en <gold>%TIME%<gray>`)."
|
||||
webhookJoinNotification: "Bloqueado: `%PLAYER%` intentó unirse durante el mantenimiento."
|
||||
webhookSingleMaintenanceActivated: "Modo de mantenimiento activado en %SERVER%`."
|
||||
webhookSingleMaintenanceDeactivated: "Modo de mantenimiento desactivado en `%SERVER%`."
|
||||
webhookSingleStarttimerStarted: "El modo de mantenimiento se activará en `%SERVER%` <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookSingleEndtimerStarted: "El modo de mantenimiento se desactivará en `%SERVER%` <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
#Used for autoupdating the language file, do not change this value.
|
||||
language-version: 2
|
||||
language-version: 5
|
||||
@@ -1,12 +1,16 @@
|
||||
#Maintenance Plugin by kennytv
|
||||
#You can use https://webui.adventure.kyori.net/ to edit and preview the formatted text
|
||||
#For a full list of formats and fancy examples, see https://docs.adventure.kyori.net/minimessage/format.html
|
||||
#You can use https://webui.advntr.dev/ to edit and preview the formatted text
|
||||
#For a full list of formats and fancy examples, see https://docs.papermc.io/adventure/minimessage/format/
|
||||
#If you want to contribute changes to the default messages, you can do so via https://crowdin.com/project/maintenance
|
||||
#Messages containing the placeholder "<prefix>" will have it replaced with the following string
|
||||
prefix: "<dark_gray>[<yellow>Maintenance<dark_gray>] "
|
||||
noPermission: "<red>Vous n'avez pas la permission d'exécuter cette commande."
|
||||
kickmessage: "<red>Le serveur est actuellement en maintenance !<br>Réessayez plus tard !"
|
||||
maintenanceActivated: "<prefix><gold>Le mode maintenance est maintenant activé."
|
||||
maintenanceDeactivated: "<prefix><gold>Le mode maintenance est maintenant désactivé."
|
||||
maintenanceStatusOn: "<prefix><gold>Le mode maintenance est actuellement <green>activé<gold>."
|
||||
maintenanceStatusOff: "<prefix><gold>Le mode maintenance est actuellement <red>désactivé<gold>."
|
||||
maintenanceStatusOnWithEndTimer: "<prefix><gold>Le mode maintenance est actuellement <green>activé<grey> et sera désactivé dans <gold>%TIME%"
|
||||
maintenanceStatusOffWithStartTimer: "<prefix><gold>Le mode maintenance est actuellement <red>désactivé<grey> et sera activé dans <gold>%TIME%"
|
||||
alreadyEnabled: "<prefix><red>La maintenance est déjà activée !"
|
||||
alreadyDisabled: "<prefix><red>La maintenance est déjà désactivée !"
|
||||
endtimerBroadcast: "<prefix><gray>Le mode maintenance sera désactivé dans <gold>%TIME%<gray>."
|
||||
@@ -29,6 +33,7 @@ reload: "<prefix><green>Fichier de configuration, liste blanche (whitelist) des
|
||||
removedMotd: "<prefix><green>Numéro de motd supprimé %INDEX%."
|
||||
removeMotdError: "<prefix><red>Vous n’avez qu’un motd, vous ne pouvez donc pas en supprimer !"
|
||||
setMotd: "<prefix><green>Ligne %LINE% du motd %INDEX%. fixée à %MOTD%"
|
||||
setReason: "<prefix><green>Raison de la maintenance configurée à : <yellow>%REASON%"
|
||||
setMotdIndexError: "<prefix><red>Vous avez actuellement %MOTDS% motd(s), vous devez donc choisir un nombre compris entre 1 et %NEWAMOUNT%."
|
||||
setMotdLineError: "<prefix><red>Le deuxième argument doit être le numéro de ligne (1 ou 2) !"
|
||||
updateDownloading: "<prefix><red><bold>Téléchargement de la mise à jour..."
|
||||
@@ -68,6 +73,8 @@ singleServerAlreadyDisabled: "<prefix><red>La maintenance est déjà désactivé
|
||||
singleServerMaintenanceList: "<prefix><gray>Serveurs proxy ayant la maintenance activée :"
|
||||
singleServerMaintenanceListEmpty: "<prefix><gray>Il n'y a pas de serveurs proxy en maintenance."
|
||||
singleServerMaintenanceListEntry: "<dark_grey>- <aqua>%SERVER%"
|
||||
singleServerMaintenanceListEntryWithStartTimer: "<dark_grey>- <grey>%SERVER% <yellow>comme dans <gold>%TIME%"
|
||||
singleServerMaintenanceListEntryWithEndTimer: "<dark_grey>- <aqua>%SERVER% <yellow>termine dans <gold>%TIME%"
|
||||
second: "seconde"
|
||||
seconds: "secondes"
|
||||
minute: "minute"
|
||||
@@ -78,27 +85,40 @@ helpHeader: "<dark_gray>========[ <yellow>%NAME% <dark_gray>| <yellow>%PAGE%/%MA
|
||||
helpPageNotFound: "<prefix><red>Il n'y a pas cette page !"
|
||||
helpNextPage: "<gray>Utilisez <aqua>/maintenance help %PAGE% <gray>pour accéder à la fenêtre d’aide suivante."
|
||||
helpAbortTimer: "<gold>/maintenance aborttimer <gray>(Si en cours d'exécution, le minuteur actuel sera abandonné)"
|
||||
helpEndtimer: "<gold>/maintenance endtimer <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpStarttimer: "<gold>/maintenance starttimer <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpScheduleTimer: "<gold>/maintenance scheduletimer <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpEndtimer: "<gold>/maintenance endtimer <désactiver dans> <gray>(Après le temps imparti, ex. 1h5m, le mode maintenance sera désactiver)"
|
||||
helpStarttimer: "<gold>/maintenance starttimer <activer dans> <gray>(Après le temps imparti, ex. 1h5m, le mode maintenance sera activé)"
|
||||
helpScheduleTimer: "<gold>/maintenance scheduletimer <activer dans> <durée de maintenance> <gray>(Après le temps imparti, ex. 1h5m, le mode maintenance sera activé)"
|
||||
helpDebug: "<gold>/maintenance debug <gray>(Active certains enregistrements de débogage)"
|
||||
helpDump: "<gold>/maintenance dump <gray>(Enregistre certaines informations sur le serveur, utilisées pour les rapports de bug)"
|
||||
helpHelp: "<gold>/maintenance help [page] <gray>(Montre cette belle fenêtre d'aide)"
|
||||
helpMotd: "<gold>/maintenance motd [timer] <gray>(Répertorie les paramètres de maintenance actuellement définis. Si vous spécifiez 'timer', les mots de la minuterie seront affichés)"
|
||||
helpRemoveMotd: "<gold>/maintenance removemotd [timer] <index> <gray>(Supprime un motd de maintenance. Si vous utilisez 'timer' comme argument, un message ping spécifique au temporisateur sera supprimé)"
|
||||
helpSetMotd: "<gold>/maintenance setmotd [timer] <index> <1/2> <message> <gray>(Définit un motd pour le mode maintenance. Si vous utilisez 'timer' comme argument, un message ping spécifique à un temporisateur sera défini)"
|
||||
helpSetReason: "<gold>/maintenance setreason <reason/none> <gray>(définit ou efface la valeur du placeholder %REASON%)"
|
||||
helpReload: "<gold>/maintenance reload <gray>(Recharge le fichier de configuration, la liste blanche, le fichier de langue et l'icône du serveur)"
|
||||
helpToggle: "<gold>/maintenance <on/off> <gray>(Active/désactive le mode maintenance)"
|
||||
helpGlobalStatus: "<gold>/maintenance status <gray>(Affiche les états de maintenance et les minuteurs actifs)"
|
||||
helpToggle: "<gold>/maintenance <on/off> [mode] <gray>(Active/désactive le mode maintenance)"
|
||||
helpUpdate: "<gold>/maintenance update <gray>(Télécharge la dernière version du plugin sur votre serveur)"
|
||||
helpWhitelist: "<gold>/maintenance whitelist <gray>(Affiche les joueurs de la liste blanche pour le mode maintenance)"
|
||||
helpWhitelistAdd: "<gold>/maintenance add <name/uuid> <gray>(Ajoute un joueur à la liste blanche de maintenance afin qu'il puisse rejoindre le serveur même si la maintenance est activée)"
|
||||
helpWhitelistRemove: "<gold>/maintenance remove <name/uuid> <gray>(Supprime un joueur de la liste blanche de maintenance)"
|
||||
#Messages for the Bungee/Velocity part, you can ignore them if you use the plugin on Paper/Sponge
|
||||
helpAbortSingleTimer: "<gold>/maintenance aborttimer [server] <gray>(Si en cours d'exécution, le minuteur actuel sera abandonné)"
|
||||
helpSingleEndtimer: "<gold>/maintenance endtimer [server] <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpSingleStarttimer: "<gold>/maintenance starttimer [server] <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleScheduleTimer: "<gold>/maintenance scheduletimer [server] <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleToggle: "<gold>/maintenance <on/off> [server] <gray>(Active/désactive le mode maintenance)"
|
||||
helpSingleEndtimer: "<gold>/maintenance endtimer [server|global] <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpSingleStarttimer: "<gold>/maintenance starttimer [server|global] [mode] <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleScheduleTimer: "<gold>/maintenance scheduletimer [server|global] [mode] <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleToggle: "<gold>/maintenance <on/off> [server|global] [mode] <gray>(Active/désactive le mode maintenance globalement, ou sur un serveur spécifique avec un mode d'affichage facultatif lors de l'activation)"
|
||||
helpStatus: "<gold>/maintenance status <gray>(Répertorie tous les serveurs mandatés actuellement en maintenance)"
|
||||
#Discord webhook messages. These use Discord markdown, not MiniMessage.
|
||||
#For timer webhooks, %TIMESTAMP% is a Unix timestamp for use in Discord timestamp tags, e.g. <t:%TIMESTAMP%:R>.
|
||||
webhookMaintenanceActivated: "\n\n**Mode maintenance activé.**"
|
||||
webhookMaintenanceDeactivated: "\n\n**Mode maintenance désactivé.**"
|
||||
webhookStarttimerStarted: "Le mode maintenance sera activé dans <t:%TIMESTAMP%:R>(`%TIME%`)."
|
||||
webhookEndtimerStarted: "Le mode maintenance sera désactivé dans <t:%TIMESTAMP%:R>(`%TIME%`)."
|
||||
webhookJoinNotification: "Connexion bloquée : `%PLAYER%` a cherché à rejoindre durant une maintenance."
|
||||
webhookSingleMaintenanceActivated: "Mode maintenance activé sur `%SERVER%`."
|
||||
webhookSingleMaintenanceDeactivated: "Mode maintenance désactivé sur `%SERVER%`."
|
||||
webhookSingleStarttimerStarted: "Le mode maintenance sera activé sur `%SERVER%` <t:%TIMESTAMP%:R>(`%TIME%`)."
|
||||
webhookSingleEndtimerStarted: "Le mode maintenance sera désactivé sur `%SERVER%` <t:%TIMESTAMP%:R>(`%TIME%`)."
|
||||
#Used for autoupdating the language file, do not change this value.
|
||||
language-version: 2
|
||||
language-version: 5
|
||||
@@ -1,12 +1,16 @@
|
||||
#Maintenance Plugin by kennytv
|
||||
#You can use https://webui.adventure.kyori.net/ to edit and preview the formatted text
|
||||
#For a full list of formats and fancy examples, see https://docs.adventure.kyori.net/minimessage/format.html
|
||||
#You can use https://webui.advntr.dev/ to edit and preview the formatted text
|
||||
#For a full list of formats and fancy examples, see https://docs.papermc.io/adventure/minimessage/format/
|
||||
#If you want to contribute changes to the default messages, you can do so via https://crowdin.com/project/maintenance
|
||||
#Messages containing the placeholder "<prefix>" will have it replaced with the following string
|
||||
prefix: "<dark_gray>[<yellow>Maintenance<dark_gray>] "
|
||||
noPermission: "<red>Nincs engedélyed a parancs használatához."
|
||||
kickmessage: "<red>A szerver karbantartás alatt áll!<br>Próbáld újra később!"
|
||||
maintenanceActivated: "<prefix><gold>Karbantartás elindítva."
|
||||
maintenanceDeactivated: "<prefix><gold>Karbantartás befejezve."
|
||||
maintenanceStatusOn: "<prefix><gold>Maintenance mode is currently <green>enabled<gold>."
|
||||
maintenanceStatusOff: "<prefix><gold>Maintenance mode is currently <red>disabled<gold>."
|
||||
maintenanceStatusOnWithEndTimer: "<prefix><gold>Maintenance mode is currently <green>enabled <grey>and will be disabled in <gold>%TIME%."
|
||||
maintenanceStatusOffWithStartTimer: "<prefix><gold>Maintenance mode is currently <red>disabled <grey>and will be enabled in <gold>%TIME%."
|
||||
alreadyEnabled: "<prefix><red>Már engedélyezve van!"
|
||||
alreadyDisabled: "<prefix><red>Már le van tiltva!"
|
||||
endtimerBroadcast: "<prefix><gray>Felléphetsz <gold>%TIME%<gray> múlva."
|
||||
@@ -29,6 +33,7 @@ reload: "<prefix><green>Újratöltve."
|
||||
removedMotd: "<prefix><green>Motd szám törölve: %INDEX%."
|
||||
removeMotdError: "<prefix><red>Csak egy motd van,nem törölheted!"
|
||||
setMotd: "<prefix><green>Sor %LINE% beállítva %INDEX%-re. motd %MOTD%-ra"
|
||||
setReason: "<prefix><green>Set maintenance reason to: <yellow>%REASON%"
|
||||
setMotdIndexError: "<prefix><red>Csak %MOTDS%-d van, úgyhogy válassz egy számot 1 és %NEWAMOUNT% között."
|
||||
setMotdLineError: "<prefix><red>Ennek egy sornak kell lennie (1 vagy 2)!"
|
||||
updateDownloading: "<prefix><red><bold>Frissítés letöltése..."
|
||||
@@ -68,6 +73,8 @@ singleServerAlreadyDisabled: "<prefix><red>A karbantartás már le van tiltva a(
|
||||
singleServerMaintenanceList: "<prefix><gray>Proxy szerverek,karbantartási móddal engedélyezve:"
|
||||
singleServerMaintenanceListEmpty: "<prefix><gray>Nincsenek proxy szerverek karbantartás alatt."
|
||||
singleServerMaintenanceListEntry: "<dark_grey>- <aqua>%SERVER%"
|
||||
singleServerMaintenanceListEntryWithStartTimer: "<dark_grey>- <grey>%SERVER% <yellow>starting in <gold>%TIME%"
|
||||
singleServerMaintenanceListEntryWithEndTimer: "<dark_grey>- <aqua>%SERVER% <yellow>ending in <gold>%TIME%"
|
||||
second: "másodperc"
|
||||
seconds: "másodperc"
|
||||
minute: "perc"
|
||||
@@ -78,27 +85,40 @@ helpHeader: "<dark_gray>========[ <yellow>%NAME% <dark_gray>| <yellow>%PAGE%/%MA
|
||||
helpPageNotFound: "<prefix><red>Nincs ilyen oldal!"
|
||||
helpNextPage: "<gray>Használd a <aqua>/maintenance help %PAGE% <gray>parancsot,hogy lapozz."
|
||||
helpAbortTimer: "<gold>/maintenance aborttimer <gray>(Az időzítő,ha fut nem lesz figelembe vévve)"
|
||||
helpEndtimer: "<gold>/maintenance endtimer <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpStarttimer: "<gold>/maintenance starttimer <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpScheduleTimer: "<gold>/maintenance scheduletimer <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpEndtimer: "<gold>/maintenance endtimer <disable in> <gray>(Azután a megadott időben pl. 1h5m, a karbantartásnak vége lesz)"
|
||||
helpStarttimer: "<gold>/maintenance starttimer <enable in> <gray>(Azután a megadott időben pl. 1h5m, karbantartás elkezdödik)"
|
||||
helpScheduleTimer: "<gold>/maintenance scheduletimer <enable in> <maintenance duration> <gray>(Azután a megadott időben pl. 1h5m, a karbantartás elkezdödik)"
|
||||
helpDebug: "<gold>/maintenance debug <gray>(Hibakeresés bekapcsolása)"
|
||||
helpDump: "<gold>/maintenance dump <gray>(A hibajelentési előzményeket törli)"
|
||||
helpHelp: "<gold>/maintenance help [page] <gray>(Megjelenítí a segítség ablakot)"
|
||||
helpMotd: "<gold>/maintenance motd [timer] <gray>(Megjeleníti a motd-ket)"
|
||||
helpRemoveMotd: "<gold>/maintenance removemotd [timer] <index> <gray>(Törli a megadott motd-t)"
|
||||
helpSetMotd: "<gold>/maintenance setmotd [timer] <index> <1/2> <message> <gray>(Beállít egy motd-t)"
|
||||
helpSetReason: "<gold>/maintenance setreason <reason/none> <gray>(Sets or clears the %REASON% placeholder value)"
|
||||
helpReload: "<gold>/maintenance reload <gray>(Újratölti a plugint)"
|
||||
helpToggle: "<gold>/maintenance <on/off> <gray>(Karbantartás mód ki-be kapcsolása)"
|
||||
helpGlobalStatus: "<gold>/maintenance status <gray>(Shows the current maintenance status and any running timers)"
|
||||
helpToggle: "<gold>/maintenance <on/off> [mode] <gray>(Enables/disables maintenance mode with an optional display mode when enabling)"
|
||||
helpUpdate: "<gold>/maintenance update <gray>(Frissíti a plugin verziót)"
|
||||
helpWhitelist: "<gold>/maintenance whitelist <gray>(Megmutatja a karbantartás fehérlistázottak listáját)"
|
||||
helpWhitelistAdd: "<gold>/maintenance add <name/uuid> <gray>(Hozzáadja a játékost a karbantartás fehérlistához)"
|
||||
helpWhitelistRemove: "<gold>/maintenance remove <name/uuid> <gray>(Kitörli a játékost a karbantartás fehérlistáról)"
|
||||
#Messages for the Bungee/Velocity part, you can ignore them if you use the plugin on Paper/Sponge
|
||||
helpAbortSingleTimer: "<gold>/maintenance aborttimer [server] <gray>(Ha van karbantartás,akkor figyelmen kívül veszi az időzítőt)"
|
||||
helpSingleEndtimer: "<gold>/maintenance endtimer [server] <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpSingleStarttimer: "<gold>/maintenance starttimer [server] <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleScheduleTimer: "<gold>/maintenance scheduletimer [server] <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleToggle: "<gold>/maintenance <on/off> [server] <gray>(Ki-be kapcsolja a karbantartás módot)"
|
||||
helpSingleEndtimer: "<gold>/maintenance endtimer [server|global] <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpSingleStarttimer: "<gold>/maintenance starttimer [server|global] [mode] <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleScheduleTimer: "<gold>/maintenance scheduletimer [server|global] [mode] <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleToggle: "<gold>/maintenance <on/off> [server|global] [mode] <gray>(Enables/disables maintenance mode globally or on a specific server with an optional display mode when enabling)"
|
||||
helpStatus: "<gold>/maintenance status <gray>(Kiírja az összes proxy szervert, ami karbantartás alatt van)"
|
||||
#Discord webhook messages. These use Discord markdown, not MiniMessage.
|
||||
#For timer webhooks, %TIMESTAMP% is a Unix timestamp for use in Discord timestamp tags, e.g. <t:%TIMESTAMP%:R>.
|
||||
webhookMaintenanceActivated: "**Maintenance mode enabled.**"
|
||||
webhookMaintenanceDeactivated: "**Maintenance mode disabled.**"
|
||||
webhookStarttimerStarted: "Maintenance mode will be enabled <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookEndtimerStarted: "Maintenance mode will be disabled <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookJoinNotification: "Blocked join: `%PLAYER%` tried to join during maintenance."
|
||||
webhookSingleMaintenanceActivated: "Maintenance mode enabled on `%SERVER%`."
|
||||
webhookSingleMaintenanceDeactivated: "Maintenance mode disabled on `%SERVER%`."
|
||||
webhookSingleStarttimerStarted: "Maintenance mode will be enabled on `%SERVER%` <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookSingleEndtimerStarted: "Maintenance mode will be disabled on `%SERVER%` <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
#Used for autoupdating the language file, do not change this value.
|
||||
language-version: 2
|
||||
language-version: 5
|
||||
@@ -1,19 +1,23 @@
|
||||
#Maintenance Plugin by kennytv
|
||||
#You can use https://webui.adventure.kyori.net/ to edit and preview the formatted text
|
||||
#For a full list of formats and fancy examples, see https://docs.adventure.kyori.net/minimessage/format.html
|
||||
#You can use https://webui.advntr.dev/ to edit and preview the formatted text
|
||||
#For a full list of formats and fancy examples, see https://docs.papermc.io/adventure/minimessage/format/
|
||||
#If you want to contribute changes to the default messages, you can do so via https://crowdin.com/project/maintenance
|
||||
#Messages containing the placeholder "<prefix>" will have it replaced with the following string
|
||||
prefix: "<dark_gray>[<yellow>Maintenance<dark_gray>] "
|
||||
noPermission: "<red>Non hai i permessi necessari per eseguire quel comando."
|
||||
noPermission: "<red>Non hai il permesso per eseguire questo comando."
|
||||
kickmessage: "<red>Il server è momentaneamente in manutenzione!<br>Riprova più tardi!"
|
||||
maintenanceActivated: "<prefix><gold>Modalità manutenzione ora attivata."
|
||||
maintenanceDeactivated: "<prefix><gold>Modalità manutenzione ora disattivata."
|
||||
maintenanceStatusOn: "<prefix><gold>Maintenance mode is currently <green>enabled<gold>."
|
||||
maintenanceStatusOff: "<prefix><gold>Maintenance mode is currently <red>disabled<gold>."
|
||||
maintenanceStatusOnWithEndTimer: "<prefix><gold>Maintenance mode is currently <green>enabled <grey>and will be disabled in <gold>%TIME%."
|
||||
maintenanceStatusOffWithStartTimer: "<prefix><gold>Maintenance mode is currently <red>disabled <grey>and will be enabled in <gold>%TIME%."
|
||||
alreadyEnabled: "<prefix><red>La modalità manutenzione è già attiva!"
|
||||
alreadyDisabled: "<prefix><red>La modalità mantenzione è già disattivata!"
|
||||
alreadyDisabled: "<prefix><red>La modalità manutenzione è già disattivata!"
|
||||
endtimerBroadcast: "<prefix><gray>La modalità manutenzione verrà disattivata tra <gold>%TIME%<gray>."
|
||||
endtimerStarted: "<prefix><green>Timer avviato: <gray>La modalità manutenzione verrà disattivata tra <gold>%TIME%<gray>."
|
||||
starttimerBroadcast: "<prefix><gray>La modalità manutenzione verrà attivata tra <gold>%TIME%<gray>."
|
||||
starttimerStarted: "<prefix><green>Timer avviato: <gray>La modalità manutenzione verrà attivata tra <gold>%TIME%<gray>."
|
||||
scheduletimerBroadcast: "<prefix><gray>Maintenance mode will be enabled in <gold>%TIME% <gray>and will last for <gold>%DURATION%<gray>."
|
||||
scheduletimerBroadcast: "<prefix><gray>La modalità manutenzione sarà abilitata tra <gold>%TIME% <gray>e durerà per <gold>%DURATION%<gray>."
|
||||
scheduletimerStarted: "<prefix><green>Timer avviato: la modalità di manutenzione <gray>sarà abilitata in <gold>%TIME% <gray>e durerà per <gold>%DURATION%<gray>."
|
||||
timerAlreadyRunning: "<prefix><red>C'è già un timer programmato!"
|
||||
timerNotRunning: "<prefix><red>Non c'è nessun timer attivo al momento."
|
||||
@@ -23,18 +27,19 @@ timerMotdDisabled: "<prefix><red>Devi impostare 'enable-timerspecific-messages'
|
||||
motdTimer: "%HOURS%:%MINUTES%:%SECONDS%"
|
||||
motdTimerNotRunning: "-"
|
||||
joinNotification: "<prefix><yellow>%PLAYER% <red>ha tentato di accedere al server."
|
||||
motdList: "<prefix><gray>Lista delle tue motds di manutenzione:"
|
||||
motdListEmpty: "<prefix><red>Non hai ancora nessuna motds di manutenzione!"
|
||||
reload: "<prefix><green>Ricaricata config, whitelistedplayers, file dei linguaggi e la icona di manutenzione."
|
||||
removedMotd: "<prefix><green>Rimossa motd numero %INDEX%."
|
||||
removeMotdError: "<prefix><red>Hai soltanto una motd, quindi non puoi rimuoverla!"
|
||||
setMotd: "<prefix><green>Impostata linea %LINE% in %INDEX%. motd manutenzione a %MOTD%"
|
||||
setMotdIndexError: "<prefix><red>Hai attualmente %MOTDS% motds, quindi devi scegliere un numero fra 1 e %NEWAMOUNT%."
|
||||
motdList: "<prefix><gray>Lista delle tue MOTD di manutenzione:"
|
||||
motdListEmpty: "<prefix><red>Non hai ancora nessuna MOTD di manutenzione!"
|
||||
reload: "<prefix><green>Ricaricata config, whitelistedplayers, file dei linguaggi e l'icona di manutenzione."
|
||||
removedMotd: "<prefix><green>Rimossa MOTD numero %INDEX%."
|
||||
removeMotdError: "<prefix><red>Hai soltanto una MOTD, quindi non puoi rimuoverla!"
|
||||
setMotd: "<prefix><green>Impostata MOTD di manutenzione numero %INDEX%, linea %LINE% a: %MOTD%"
|
||||
setReason: "<prefix><green>Set maintenance reason to: <yellow>%REASON%"
|
||||
setMotdIndexError: "<prefix><red>Hai attualmente %MOTDS% MOTD, devi scegliere un numero tra 1 e %NEWAMOUNT%."
|
||||
setMotdLineError: "<prefix><red>Il secondo argomento deve corrispondere alla linea (1 o 2)!"
|
||||
updateDownloading: "<prefix><red><bold>Scaricando aggiornamento..."
|
||||
updateDownloading: "<prefix><red><bold> Sto scaricando l'aggiornamento..."
|
||||
updateFailed: "<prefix><dark_red><bold>Aggiornamento non riuscito!"
|
||||
updateFinished: "<prefix><green><bold>L'aggiornamento è completato! Per prevenire problemi e completare l'aggiornamento dovresti riavviare il server!"
|
||||
whitelistedPlayers: "<prefix><gold>Player in whitelist per la manutenzione:"
|
||||
whitelistedPlayers: "<prefix><gold>Giocatori in whitelist per la manutenzione:"
|
||||
whitelistedPlayersFormat: "<dark_gray>- <yellow>%NAME% <dark_gray>(<gray>%UUID%<dark_gray>)"
|
||||
whitelistAdded: "<prefix><green>Aggiunto <aqua>%PLAYER% <green>alla whitelist della manutenzione!"
|
||||
whitelistAlreadyAdded: "<prefix><aqua>%PLAYER% <red>è già nella whitelist della manutenzione!"
|
||||
@@ -42,32 +47,34 @@ whitelistRemoved: "<prefix><green>Rimosso <aqua>%PLAYER% <green>dalla lista dell
|
||||
whitelistNotFound: "<prefix><red>Questo giocatore non fa parte della whitelist della manutenzione!"
|
||||
whitelistEmpty: "<prefix><red>La whitelist di manutenzione è vuota! Usa <yellow>/maintenance add <player/uuid> <red>per aggiungere qualcuno!"
|
||||
playerNotFound: "<prefix><red>Nessun giocatore con questo username è mai entrato nel server."
|
||||
playerNotFoundUuid: "<prefix><red>Nessun player con quell'uuid è stato trovato."
|
||||
playerNotFoundUuid: "<prefix><red>Nessun giocatore con quell'uuid è stato trovato."
|
||||
playerNotOnline: "<prefix><red>Non c'è nessun giocatore online con quell'username."
|
||||
offlinePlayerFetchError: "<prefix><red>C'è stato un errore cercando il player offline. Riprova più tardi."
|
||||
offlinePlayerFetchError: "<prefix><red>C'è stato un errore cercando il giocatore offline. Riprova più tardi."
|
||||
invalidUuid: "<prefix><red>L'uuid presenta un formato invalido!"
|
||||
#Messages for the Bungee/Velocity part, you can ignore them if you use the plugin on Paper/Sponge
|
||||
sentToWaitingServer: "<prefix><red>Sei stato inviato su un server d'attesa!"
|
||||
forceWaitingServer: "<prefix><red>Non puoi abbandonare il server d'attesa durante una manutenzione!"
|
||||
serverNotFound: "<prefix><red>Nessun server con questo nome è registrato sulla proxy!"
|
||||
serverNotFound: "<prefix><red>Nessun server con questo nome è registrato sul proxy server!"
|
||||
singleTimerAlreadyRunning: "<prefix><red>È già presente un timer pianificato per quel server!"
|
||||
singleTimerCancelled: "<prefix><red>Il timer attuale per il server <yellow>%SERVER% <red>è stato rimosso."
|
||||
singleTimerNotRunning: "<prefix><red>Non c'è nessun timer correntemente attivo per quel server."
|
||||
singleEndtimerBroadcast: "<prefix><gray>La modalità manutenzione per il server %SERVER% verrà disabilitata tra <gold>%TIME%<gray>."
|
||||
singleEndtimerStarted: "<prefix><green>Timer avviato: <gray>La modalità manutenzione per il server %SERVER% verrà disattivata tra <gold>%TIME%<gray>."
|
||||
singleStarttimerBroadcast: "<prefix><gray>La modalità manutenzione per il server %SERVER% verrà disabilitata tra <gold>%TIME%<gray>."
|
||||
singleStarttimerBroadcast: "<prefix><gray>La modalità manutenzione per il server %SERVER% verrà attivata tra <gold>%TIME%<gray>."
|
||||
singleStarttimerStarted: "<prefix><green>Timer avviato: <gray>La modalità manutenzione per il server %SERVER% verrà attivata tra <gold>%TIME%<gray>."
|
||||
singleScheduletimerBroadcast: "<prefix><gray>Maintenance mode on server %SERVER% will be enabled in <gold>%TIME% <gray>and will last for <gold>%DURATION%<gray>."
|
||||
singleScheduletimerBroadcast: "<prefix><gray>La modalità manutenzione sul server %SERVER% sarà abilitata tra <gold>%TIME% <gray>e durerà per <gold>%DURATION%<gray>."
|
||||
singleScheduletimerStarted: "<prefix><green>Timer avviato: <gray>Modalità di manutenzione sul server %SERVER% sarà attivato in <gold>%TIME% <gray>e durerà per <gold>%DURATION%<gray>."
|
||||
singleMaintenanceKick: "<prefix><red>Il server %SERVER% è in manutenzione al momento! Ritenta più tardi!"
|
||||
singleMaintenanceKickComplete: "<red>Il server %SERVER% è in manutenzione!<br>Prova a rientrare per accedere ad un'altro server!"
|
||||
singleMaintenanceKickComplete: "<red>Il server %SERVER% è in manutenzione!<br>Prova a rientrare per accedere a un altro server!"
|
||||
singleMaintenanceActivated: "<prefix><gold>La modalità manutenzione è ora attiva sul server <yellow>%SERVER%<gold>."
|
||||
singleMaintenanceDeactivated: "<prefix><gold>La modalità manutenzione è ora disattivata sul server <yellow>%SERVER%<gold>."
|
||||
singleServerAlreadyEnabled: "<prefix><red>La modalità manutenzione è già attiva sul server <yellow>%SERVER%<red>!"
|
||||
singleServerAlreadyDisabled: "<prefix><red>La modalità manutenzione è già disattivata sul server <yellow>%SERVER%<red>!"
|
||||
singleServerMaintenanceList: "<prefix><gray>Servers sotto proxy che hanno la modalità manutenzione attiva:"
|
||||
singleServerMaintenanceListEmpty: "<prefix><gray>Nessun server sotto proxy ha la modalità manutenzione attiva."
|
||||
singleServerMaintenanceList: "<prefix><gray>Server collegati con manutenzione attiva:"
|
||||
singleServerMaintenanceListEmpty: "<prefix><gray>Nessun server sotto proxy server ha la modalità manutenzione attiva."
|
||||
singleServerMaintenanceListEntry: "<dark_grey>- <aqua>%SERVER%"
|
||||
singleServerMaintenanceListEntryWithStartTimer: "<dark_grey>- <grey>%SERVER% <yellow>starting in <gold>%TIME%"
|
||||
singleServerMaintenanceListEntryWithEndTimer: "<dark_grey>- <aqua>%SERVER% <yellow>ending in <gold>%TIME%"
|
||||
second: "secondo"
|
||||
seconds: "secondi"
|
||||
minute: "minuto"
|
||||
@@ -78,27 +85,40 @@ helpHeader: "<dark_gray>========[ <yellow>%NAME% <dark_gray>| <yellow>%PAGE%/%MA
|
||||
helpPageNotFound: "<prefix><red>Non c'è alcuna pagina con quel numero!"
|
||||
helpNextPage: "<gray>Usa <aqua>/maintenance help %PAGE% <gray>per andare alla prossima pagina d'aiuto."
|
||||
helpAbortTimer: "<gold>/maintenance aborttimer <gray>(Se attivo, l'attuale timer verrà fermato)"
|
||||
helpEndtimer: "<gold>/maintenance endtimer <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpStarttimer: "<gold>/maintenance starttimer <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpScheduleTimer: "<gold>/maintenance scheduletimer <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpDebug: "<gold>/maintenance debug <gray>(Enables some debug logging)"
|
||||
helpEndtimer: "<gold>/maintenance endtimer <tempo di disattivazione> <gray>(Dopo il tempo specificato, es. 1h5m, la modalità manutenzione verrà disattivata)"
|
||||
helpStarttimer: "<gold>/maintenance starttimer <tempo di attivazione> <gray>(Dopo il tempo specificato, es. 1h5m, la modalità manutenzione verrà attivata)"
|
||||
helpScheduleTimer: "<gold>/maintenance scheduletimer <tempo di attivazione> <durata manutenzione> <gray>(Dopo il tempo specificato, es. 1h5m, la modalità manutenzione verrà attivata e durerà per il tempo indicato)"
|
||||
helpDebug: "<gold>/maintenance debug <gray>(Attiva la registrazione di debug)"
|
||||
helpDump: "<gold>/maintenance dump <gray>(Copia alcune informazioni dal server, usato per segnalare problemi)"
|
||||
helpHelp: "<gold>/maintenance help [page] <gray>(Mostra questa fantastica pagina d'aiuto)"
|
||||
helpMotd: "<gold>/maintenance motd [timer] <gray>(Elenca le attuali motds di manutenzione impostate. Se specifichi 'timer', verranno mostrati i motds dei timer)"
|
||||
helpRemoveMotd: "<gold>/maintenance removemotd [timer] <index> <gray>(Rimuove una motd di manutenzione. Se specifichi 'timer' come argomento, un messaggio specifico al timer verrà rimosso)"
|
||||
helpSetMotd: "<gold>/maintenance setmotd [timer] <index> <1/2> <message> <gray>(Imposta il motd per la manutenzione. Se specifichi 'timer' come argomento, un messaggio specifico al timer verrà impostato)"
|
||||
helpSetReason: "<gold>/maintenance setreason <reason/none> <gray>(Sets or clears the %REASON% placeholder value)"
|
||||
helpReload: "<gold>/maintenance reload <gray>(Ricarica il file di configurazione, whitelist server e icona)"
|
||||
helpToggle: "<gold>/maintenance <on/off> <gray>(Abilita/Disabilita modalità manutenzione)"
|
||||
helpGlobalStatus: "<gold>/maintenance status <gray>(Shows the current maintenance status and any running timers)"
|
||||
helpToggle: "<gold>/maintenance <on/off> [mode] <gray>(Enables/disables maintenance mode with an optional display mode when enabling)"
|
||||
helpUpdate: "<gold>/maintenance update <gray>(Scarica dall'esterno l'ultima versione del plugin nel server)"
|
||||
helpWhitelist: "<gold>/maintenance whitelist <gray>(Elenca tutti i giocatori nella whitelist per la manutenzione)"
|
||||
helpWhitelistAdd: "<gold>/maintenance add <name/uuid> <gray>(Aggiunge il giocatore alla whitelist di manutenzione, così possono entrare in un server durante la manutenzione)"
|
||||
helpWhitelistRemove: "<gold>/maintenance remove <name/uuid> <gray>(Rimuovi il player dalla whitelist della manutenzione)"
|
||||
helpWhitelistRemove: "<gold>/maintenance remove <name/uuid> <gray>(Rimuovi il giocatore dalla whitelist della manutenzione)"
|
||||
#Messages for the Bungee/Velocity part, you can ignore them if you use the plugin on Paper/Sponge
|
||||
helpAbortSingleTimer: "<gold>/maintenance aborttimer [server] <gray>(Se attivo, il timer attuale verrà fermato)"
|
||||
helpSingleEndtimer: "<gold>/maintenance endtimer [server] <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpSingleStarttimer: "<gold>/maintenance starttimer [server] <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleScheduleTimer: "<gold>/maintenance scheduletimer [server] <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleToggle: "<gold>/maintenance <on/off> [server] <gray>(Abilita/disabilita la modalità di manutenzione)"
|
||||
helpSingleEndtimer: "<gold>/maintenance endtimer [server|global] <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpSingleStarttimer: "<gold>/maintenance starttimer [server|global] [mode] <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleScheduleTimer: "<gold>/maintenance scheduletimer [server|global] [mode] <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleToggle: "<gold>/maintenance <on/off> [server|global] [mode] <gray>(Enables/disables maintenance mode globally or on a specific server with an optional display mode when enabling)"
|
||||
helpStatus: "<gold>/maintenance status <gray>(Elenca tutti i server sotto proxy che hanno la modalità manutenzione attiva)"
|
||||
#Discord webhook messages. These use Discord markdown, not MiniMessage.
|
||||
#For timer webhooks, %TIMESTAMP% is a Unix timestamp for use in Discord timestamp tags, e.g. <t:%TIMESTAMP%:R>.
|
||||
webhookMaintenanceActivated: "**Maintenance mode enabled.**"
|
||||
webhookMaintenanceDeactivated: "**Maintenance mode disabled.**"
|
||||
webhookStarttimerStarted: "Maintenance mode will be enabled <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookEndtimerStarted: "Maintenance mode will be disabled <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookJoinNotification: "Blocked join: `%PLAYER%` tried to join during maintenance."
|
||||
webhookSingleMaintenanceActivated: "Maintenance mode enabled on `%SERVER%`."
|
||||
webhookSingleMaintenanceDeactivated: "Maintenance mode disabled on `%SERVER%`."
|
||||
webhookSingleStarttimerStarted: "Maintenance mode will be enabled on `%SERVER%` <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookSingleEndtimerStarted: "Maintenance mode will be disabled on `%SERVER%` <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
#Used for autoupdating the language file, do not change this value.
|
||||
language-version: 2
|
||||
language-version: 5
|
||||
@@ -1,12 +1,16 @@
|
||||
#Maintenance Plugin by kennytv
|
||||
#You can use https://webui.adventure.kyori.net/ to edit and preview the formatted text
|
||||
#For a full list of formats and fancy examples, see https://docs.adventure.kyori.net/minimessage/format.html
|
||||
#You can use https://webui.advntr.dev/ to edit and preview the formatted text
|
||||
#For a full list of formats and fancy examples, see https://docs.papermc.io/adventure/minimessage/format/
|
||||
#If you want to contribute changes to the default messages, you can do so via https://crowdin.com/project/maintenance
|
||||
#Messages containing the placeholder "<prefix>" will have it replaced with the following string
|
||||
prefix: "<dark_gray>[<yellow>Maintenance<dark_gray>] "
|
||||
noPermission: "<red>このコマンドを実行する権限がありません。"
|
||||
kickmessage: "<red>メンテナンス中です。<br>しばらくたってから、もう一度お試しください"
|
||||
maintenanceActivated: "<prefix><green>メンテナンスを有効にしました。"
|
||||
maintenanceDeactivated: "<prefix><yellow>メンテナンスを解除しました。"
|
||||
maintenanceStatusOn: "<prefix><gold>Maintenance mode is currently <green>enabled<gold>."
|
||||
maintenanceStatusOff: "<prefix><gold>Maintenance mode is currently <red>disabled<gold>."
|
||||
maintenanceStatusOnWithEndTimer: "<prefix><gold>Maintenance mode is currently <green>enabled <grey>and will be disabled in <gold>%TIME%."
|
||||
maintenanceStatusOffWithStartTimer: "<prefix><gold>Maintenance mode is currently <red>disabled <grey>and will be enabled in <gold>%TIME%."
|
||||
alreadyEnabled: "<prefix><red>メンテナンスは既に有効です。"
|
||||
alreadyDisabled: "<prefix><red>メンテナンスは既に解除されています。"
|
||||
endtimerBroadcast: "<prefix><gray>メンテナンスモードは、<gold>%TIME%<gray>で無効になります。"
|
||||
@@ -29,6 +33,7 @@ reload: "<prefix><green>再読み込みが完了しました。"
|
||||
removedMotd: "<prefix><green>motd番号%INDEX%を削除しました。"
|
||||
removeMotdError: "<prefix><red>motdは1つしかないので、削除することはできません。"
|
||||
setMotd: "<prefix><greenINDEX%.の行 %LINE% を %MOTD% に設定します。"
|
||||
setReason: "<prefix><green>Set maintenance reason to: <yellow>%REASON%"
|
||||
setMotdIndexError: "<prefix><red>現在、%MOTDS%のMOTDを持っているので、1から%NEWAMOUNT%の間の数字を選ぶ必要があります。"
|
||||
setMotdLineError: "<prefix><red>第2引数には、行番号(1または2)を指定する必要があります。"
|
||||
updateDownloading: "<prefix><red><bold>アップデートをダウンロード中です..."
|
||||
@@ -68,6 +73,8 @@ singleServerAlreadyDisabled: "<prefix><yellow>%SERVER%<red> のメンテナン
|
||||
singleServerMaintenanceList: "<prefix><gray>プロキシサーバーで、メンテナンスが有効になっているもの: "
|
||||
singleServerMaintenanceListEmpty: "<prefix><gray>メンテナンス中のプロキシサーバーはありません。"
|
||||
singleServerMaintenanceListEntry: "<dark_grey>- <aqua>%SERVER%"
|
||||
singleServerMaintenanceListEntryWithStartTimer: "<dark_grey>- <grey>%SERVER% <yellow>starting in <gold>%TIME%"
|
||||
singleServerMaintenanceListEntryWithEndTimer: "<dark_grey>- <aqua>%SERVER% <yellow>ending in <gold>%TIME%"
|
||||
second: "秒"
|
||||
seconds: "秒"
|
||||
minute: "分"
|
||||
@@ -78,27 +85,40 @@ helpHeader: "<dark_gray>========[ <yellow>%NAME% <dark_gray>| <yellow>%PAGE%/%MA
|
||||
helpPageNotFound: "<prefix><red>その番号のページがありません。"
|
||||
helpNextPage: "<gray>Use <aqua>/maintenance help %PAGE% <gray>で次のヘルプウィンドウに移動します。"
|
||||
helpAbortTimer: "<gold>/maintenance aborttimer <gray>(タイマーを実行していた場合、タイマーの実行を中止します。)"
|
||||
helpEndtimer: "<gold>/maintenance endtimer <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpStarttimer: "<gold>/maintenance starttimer <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpScheduleTimer: "<gold>/maintenance scheduletimer <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpEndtimer: "<gold>/maintenance endtimer <disable in> <gray>(指定された時間(例えば1時間5分)が経過すると、メンテナンス・モードは無効になる)"
|
||||
helpStarttimer: "<gold>/maintenance starttimer <enable in> <gray>(指定された時間後、例えば1時間5分後にメンテナンス・モードが有効になる)"
|
||||
helpScheduleTimer: "<gold>/maintenance scheduletimer <enable in> <maintenance duration> <gray>(指定された時間(例えば1時間5分)が経過すると、メンテナンスモードが有効になる)"
|
||||
helpDebug: "<gold>/maintenance debug <gray>(デバッグロギングを有効にします。)"
|
||||
helpDump: "<gold>/maintenance dump <gray>(バグレポートに使用される、いくつかのサーバ情報をダンプします。)"
|
||||
helpHelp: "<gold>/maintenance help [page] <gray>(このようなヘルプページが表示されます。)"
|
||||
helpMotd: "<gold>/maintenance motd [timer] <gray>(現在設定されているメンテナンスMOTDを一覧表示します。'timer'を指定した場合は、タイマーの場合のMOTDを表示します。)"
|
||||
helpRemoveMotd: "<gold>/maintenance removemotd [timer] <index> <gray>(メンテナンスの場合に使用されるMOTDを削除します。引数として'timer'を使用した場合にはタイマー固有のピングメッセージが表示されます。)"
|
||||
helpSetMotd: "<gold>/maintenance setmotd [timer] <index> <1/2> <message> <gray>(メンテナンスの場合に使用されるMOTDを設定します。引数として'timer'を使用した場合にはタイマー固有のピングメッセージが表示されます。)"
|
||||
helpSetReason: "<gold>/maintenance setreason <reason/none> <gray>(Sets or clears the %REASON% placeholder value)"
|
||||
helpReload: "<gold>/maintenance reload <gray>(設定ファイル、ホワイトリストファイル、サーバーアイコンを再読み込みします。)"
|
||||
helpToggle: "<gold>/maintenance <on/off> <gray>(メンテナンスモードの有効/解除を設定します。)"
|
||||
helpGlobalStatus: "<gold>/maintenance status <gray>(Shows the current maintenance status and any running timers)"
|
||||
helpToggle: "<gold>/maintenance <on/off> [mode] <gray>(Enables/disables maintenance mode with an optional display mode when enabling)"
|
||||
helpUpdate: "<gold>/maintenance update <gray>(最新版のプラグインをサーバーにリモートでダウンロードします。)"
|
||||
helpWhitelist: "<gold>/maintenance whitelist <gray>(メンテナンスモードのホワイトリストに登録されているすべてのプレーヤーを表示します。)"
|
||||
helpWhitelistAdd: "<gold>/maintenance add <name/uuid> <gray>(メンテナンスのホワイトリストにユーザーを追加し、メンテナンスが有効な状態でもサーバーに参加できるようにします。)"
|
||||
helpWhitelistRemove: "<gold>/maintenance remove <name/uuid> <gray>(メンテナンスのホワイトリストからプレーヤーを削除します。)"
|
||||
#Messages for the Bungee/Velocity part, you can ignore them if you use the plugin on Paper/Sponge
|
||||
helpAbortSingleTimer: "<gold>/maintenance aborttimer [server] <gray>(もし、タイマーが実行中の場合、現在のタイマーを中止します。)"
|
||||
helpSingleEndtimer: "<gold>/maintenance endtimer [server] <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpSingleStarttimer: "<gold>/maintenance starttimer [server] <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleScheduleTimer: "<gold>/maintenance scheduletimer [server] <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleToggle: "<gold>/maintenance <on/off> [server] <gray>(メンテナンスモードの有効化/無効化を設定します。)"
|
||||
helpSingleEndtimer: "<gold>/maintenance endtimer [server|global] <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpSingleStarttimer: "<gold>/maintenance starttimer [server|global] [mode] <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleScheduleTimer: "<gold>/maintenance scheduletimer [server|global] [mode] <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleToggle: "<gold>/maintenance <on/off> [server|global] [mode] <gray>(Enables/disables maintenance mode globally or on a specific server with an optional display mode when enabling)"
|
||||
helpStatus: "<gold>/maintenance status <gray>(現在メンテナンス中のプロキシサーバーをすべてリストアップします。)"
|
||||
#Discord webhook messages. These use Discord markdown, not MiniMessage.
|
||||
#For timer webhooks, %TIMESTAMP% is a Unix timestamp for use in Discord timestamp tags, e.g. <t:%TIMESTAMP%:R>.
|
||||
webhookMaintenanceActivated: "**Maintenance mode enabled.**"
|
||||
webhookMaintenanceDeactivated: "**Maintenance mode disabled.**"
|
||||
webhookStarttimerStarted: "Maintenance mode will be enabled <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookEndtimerStarted: "Maintenance mode will be disabled <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookJoinNotification: "Blocked join: `%PLAYER%` tried to join during maintenance."
|
||||
webhookSingleMaintenanceActivated: "Maintenance mode enabled on `%SERVER%`."
|
||||
webhookSingleMaintenanceDeactivated: "Maintenance mode disabled on `%SERVER%`."
|
||||
webhookSingleStarttimerStarted: "Maintenance mode will be enabled on `%SERVER%` <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookSingleEndtimerStarted: "Maintenance mode will be disabled on `%SERVER%` <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
#Used for autoupdating the language file, do not change this value.
|
||||
language-version: 2
|
||||
language-version: 5
|
||||
@@ -1,12 +1,16 @@
|
||||
#Maintenance Plugin by kennytv
|
||||
#You can use https://webui.adventure.kyori.net/ to edit and preview the formatted text
|
||||
#For a full list of formats and fancy examples, see https://docs.adventure.kyori.net/minimessage/format.html
|
||||
#You can use https://webui.advntr.dev/ to edit and preview the formatted text
|
||||
#For a full list of formats and fancy examples, see https://docs.papermc.io/adventure/minimessage/format/
|
||||
#If you want to contribute changes to the default messages, you can do so via https://crowdin.com/project/maintenance
|
||||
#Messages containing the placeholder "<prefix>" will have it replaced with the following string
|
||||
prefix: "<dark_gray>[<yellow>Maintenance<dark_gray>] "
|
||||
noPermission: "<red>권한이 없습니다."
|
||||
kickmessage: "<red>서버가 현재 점검 중입니다!<br>나중에 다시 시도하세요!"
|
||||
maintenanceActivated: "<prefix><gold>유지 관리 모드가 활성화되었습니다."
|
||||
maintenanceDeactivated: "<prefix><gold>유지 관리 모드가 비활성화되었습니다."
|
||||
maintenanceStatusOn: "<prefix><gold>Maintenance mode is currently <green>enabled<gold>."
|
||||
maintenanceStatusOff: "<prefix><gold>Maintenance mode is currently <red>disabled<gold>."
|
||||
maintenanceStatusOnWithEndTimer: "<prefix><gold>Maintenance mode is currently <green>enabled <grey>and will be disabled in <gold>%TIME%."
|
||||
maintenanceStatusOffWithStartTimer: "<prefix><gold>Maintenance mode is currently <red>disabled <grey>and will be enabled in <gold>%TIME%."
|
||||
alreadyEnabled: "<prefix><red>유지 관리가 이미 활성화되었습니다!"
|
||||
alreadyDisabled: "<prefix><red>유지보수가 이미 비활성화되었습니다!"
|
||||
endtimerBroadcast: "<prefix><gray>유지 관리 모드는<gold>%TIME%<gray>에 비활성화됩니다.."
|
||||
@@ -29,6 +33,7 @@ reload: "<prefix><green>설정, 화이트리스트 플레이어, 언어 파일
|
||||
removedMotd: "<prefix><green>%INDEX%번째 motd를 삭제했습니다."
|
||||
removeMotdError: "<prefix><red>motd가 하나만 있으므로 제거할 수 없습니다!"
|
||||
setMotd: "<prefix><green>INDEX%의 %LINE% 줄을 %MOTD%로 설정합니다."
|
||||
setReason: "<prefix><green>Set maintenance reason to: <yellow>%REASON%"
|
||||
setMotdIndexError: "<prefix><red>현재 %MOTDS% motd가 있으므로 1에서 %NEWAMOUNT% 사이의 숫자를 선택해야 합니다."
|
||||
setMotdLineError: "<prefix><red>두 번째 인수는 줄 번호(1 또는 2)여야 합니다!"
|
||||
updateDownloading: "<prefix><red><bold>업데이트 다운로드 중..."
|
||||
@@ -68,6 +73,8 @@ singleServerAlreadyDisabled: "<prefix><red>Maintenance is already disabled on se
|
||||
singleServerMaintenanceList: "<prefix><gray>Proxied servers, that have maintenance enabled:"
|
||||
singleServerMaintenanceListEmpty: "<prefix><gray>There are no proxied servers that are under maintenance."
|
||||
singleServerMaintenanceListEntry: "<dark_grey>- <aqua>%SERVER%"
|
||||
singleServerMaintenanceListEntryWithStartTimer: "<dark_grey>- <grey>%SERVER% <yellow>starting in <gold>%TIME%"
|
||||
singleServerMaintenanceListEntryWithEndTimer: "<dark_grey>- <aqua>%SERVER% <yellow>ending in <gold>%TIME%"
|
||||
second: "second"
|
||||
seconds: "seconds"
|
||||
minute: "minute"
|
||||
@@ -87,18 +94,31 @@ helpHelp: "<gold>/maintenance help [page] <gray>(Shows this beautiful help windo
|
||||
helpMotd: "<gold>/maintenance motd [timer] <gray>(Lists the currently set maintenance motds. If specifying 'timer', the timer motds are shown)"
|
||||
helpRemoveMotd: "<gold>/maintenance removemotd [timer] <index> <gray>(Removes a maintenance motd. If using 'timer' as an argument, a timerspecific pingmessage will be removed)"
|
||||
helpSetMotd: "<gold>/maintenance setmotd [timer] <index> <1/2> <message> <gray>(Sets a motd for maintenance mode. If using 'timer' as an argument, a timerspecific pingmessage will be set)"
|
||||
helpSetReason: "<gold>/maintenance setreason <reason/none> <gray>(Sets or clears the %REASON% placeholder value)"
|
||||
helpReload: "<gold>/maintenance reload <gray>(Reloads the config file, whitelist file and the server-icon)"
|
||||
helpToggle: "<gold>/maintenance <on/off> <gray>(Enables/disables maintenance mode)"
|
||||
helpGlobalStatus: "<gold>/maintenance status <gray>(Shows the current maintenance status and any running timers)"
|
||||
helpToggle: "<gold>/maintenance <on/off> [mode] <gray>(Enables/disables maintenance mode with an optional display mode when enabling)"
|
||||
helpUpdate: "<gold>/maintenance update <gray>(Remotely downloads the newest version of the plugin onto your server)"
|
||||
helpWhitelist: "<gold>/maintenance whitelist <gray>(Shows all whitelisted players for the maintenance mode)"
|
||||
helpWhitelistAdd: "<gold>/maintenance add <name/uuid> <gray>(Adds the player to the maintenance whitelist, so they can join the server even though maintenance is enabled)"
|
||||
helpWhitelistRemove: "<gold>/maintenance remove <name/uuid> <gray>(Removes the player from the maintenance whitelist)"
|
||||
#Messages for the Bungee/Velocity part, you can ignore them if you use the plugin on Paper/Sponge
|
||||
helpAbortSingleTimer: "<gold>/maintenance aborttimer [server] <gray>(If running, the current timer will be aborted)"
|
||||
helpSingleEndtimer: "<gold>/maintenance endtimer [server] <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpSingleStarttimer: "<gold>/maintenance starttimer [server] <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleScheduleTimer: "<gold>/maintenance scheduletimer [server] <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleToggle: "<gold>/maintenance <on/off> [server] <gray>(Enables/disables maintenance mode)"
|
||||
helpSingleEndtimer: "<gold>/maintenance endtimer [server|global] <disable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be disabled)"
|
||||
helpSingleStarttimer: "<gold>/maintenance starttimer [server|global] [mode] <enable in> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleScheduleTimer: "<gold>/maintenance scheduletimer [server|global] [mode] <enable in> <maintenance duration> <gray>(After the given time, e.g. 1h5m, maintenance mode will be enabled)"
|
||||
helpSingleToggle: "<gold>/maintenance <on/off> [server|global] [mode] <gray>(Enables/disables maintenance mode globally or on a specific server with an optional display mode when enabling)"
|
||||
helpStatus: "<gold>/maintenance status <gray>(Lists all proxied servers, that are currently under maintenance)"
|
||||
#Discord webhook messages. These use Discord markdown, not MiniMessage.
|
||||
#For timer webhooks, %TIMESTAMP% is a Unix timestamp for use in Discord timestamp tags, e.g. <t:%TIMESTAMP%:R>.
|
||||
webhookMaintenanceActivated: "**Maintenance mode enabled.**"
|
||||
webhookMaintenanceDeactivated: "**Maintenance mode disabled.**"
|
||||
webhookStarttimerStarted: "Maintenance mode will be enabled <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookEndtimerStarted: "Maintenance mode will be disabled <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookJoinNotification: "Blocked join: `%PLAYER%` tried to join during maintenance."
|
||||
webhookSingleMaintenanceActivated: "Maintenance mode enabled on `%SERVER%`."
|
||||
webhookSingleMaintenanceDeactivated: "Maintenance mode disabled on `%SERVER%`."
|
||||
webhookSingleStarttimerStarted: "Maintenance mode will be enabled on `%SERVER%` <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
webhookSingleEndtimerStarted: "Maintenance mode will be disabled on `%SERVER%` <t:%TIMESTAMP%:R> (`%TIME%`)."
|
||||
#Used for autoupdating the language file, do not change this value.
|
||||
language-version: 2
|
||||
language-version: 5
|
||||
Loaded 100 of 146 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user