Compare commits

...
499 Commits
Author SHA1 Message Date
Andre601 e69c94da06 Fix wrong VersionUplaoder command 2026-07-03 18:13:53 +02:00
Andre601 7cd3835785 Update SECURITY.md and versions in uploader 2026-07-03 17:56:31 +02:00
Andre601 8c36d27360 Version 5.9.0 2026-07-03 17:47:17 +02:00
Andre601 5329ed1b88 Merge pull request 'Update dependency me.clip:placeholderapi to v2.12.3' (#602) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/602
2026-07-03 14:32:32 +02:00
Andre601's Bot d66494f4dd Update dependency me.clip:placeholderapi to v2.12.3 2026-07-03 12:02:25 +00:00
Andre601 d03ebf5fb1 Merge pull request 'Update dependency org.apache.httpcomponents.client5:httpclient5 to v5.6.2' (#601) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/601
2026-06-30 14:22:34 +02:00
Andre601's Bot c7ae2c130f Update dependency org.apache.httpcomponents.client5:httpclient5 to v5.6.2 2026-06-30 12:03:47 +00:00
Andre601 017a1893c4 Merge pull request 'Update all non-major dependencies to v2.0.0-beta.17' (#600) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/600
2026-06-29 14:09:50 +02:00
Andre601's Bot 7a3af4cb26 Update all non-major dependencies to v2.0.0-beta.17 2026-06-29 12:04:34 +00:00
Andre601 0d05f2fe1d Merge pull request 'Update all non-major dependencies' (#599) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/599
2026-06-27 15:27:15 +02:00
Andre601's Bot 0acb097439 Update all non-major dependencies 2026-06-27 12:04:09 +00:00
Andre601 8a67fbead8 Merge pull request 'Update dependency ch.qos.logback:logback-classic to v1.5.36' (#598) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/598
2026-06-26 14:26:16 +02:00
Andre601's Bot 8f3252d66a Update dependency ch.qos.logback:logback-classic to v1.5.36 2026-06-26 12:04:11 +00:00
Andre601 7668a680c4 Merge pull request 'Update dependency ch.qos.logback:logback-classic to v1.5.35' (#597) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/597
2026-06-23 14:10:30 +02:00
Andre601's Bot 944e5a7262 Update dependency ch.qos.logback:logback-classic to v1.5.35 2026-06-23 12:05:36 +00:00
Andre601 d7cefb77ce Add target and rel to link 2026-06-22 00:50:43 +02:00
Andre601 e7a44711c0 Use v3 of Modrinth API 2026-06-22 00:47:12 +02:00
Andre601 96750ec5fd Merge pull request 'Update all non-major dependencies' (#594) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/594
2026-06-21 13:15:55 +02:00
Andre601's Bot 16500f29e4 Update all non-major dependencies 2026-06-20 14:14:12 +00:00
Andre601 25e433d0ed Display downloads remaining for 100k 2026-06-20 01:09:55 +02:00
Andre601 21430e9a4e Use codeberg-small for longer runtime 2026-06-19 16:42:27 +02:00
Andre601 de5455ab39 Merge pull request 'Update https://code.forgejo.org/actions/checkout action to v7' (#593) from renovate/https-code.forgejo.org-actions-checkout-7.x into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/593
2026-06-19 15:26:46 +02:00
Andre601 a52ca1d8be .forgejo/workflows/pr_preview.yml aktualisiert 2026-06-19 15:26:40 +02:00
Andre601 34d8048dd4 Merge pull request 'Update all non-major dependencies to v2.0.0-beta.16' (#592) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/592
2026-06-19 14:12:18 +02:00
Andre601's Bot 3b1e676888 Update https://code.forgejo.org/actions/checkout action to v7 2026-06-19 12:06:42 +00:00
Andre601's Bot 9332a3e342 Update all non-major dependencies to v2.0.0-beta.16 2026-06-19 12:06:33 +00:00
Andre601 eacf227650 Merge pull request 'Update all non-major dependencies to v1.1.3' (#591) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/591
2026-06-16 14:45:50 +02:00
Andre601's Bot 56131f4478 Update all non-major dependencies to v1.1.3 2026-06-16 12:02:51 +00:00
Andre601 b362c34141 Use DefaultExpressionParserEngine 2026-06-09 21:55:46 +02:00
Andre601 41718611e7 Merge pull request 'Update all non-major dependencies to v5.4.0' (#590) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/590
2026-06-09 14:13:18 +02:00
Andre601's Bot a92d6716e4 Update all non-major dependencies to v5.4.0 2026-06-09 12:04:50 +00:00
Andre601 768509100d Merge pull request 'Update dependency ch.qos.logback:logback-classic to v1.5.34' (#588) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/588
2026-06-01 14:17:51 +02:00
Andre601's Bot a64ad0cfa0 Update dependency ch.qos.logback:logback-classic to v1.5.34 2026-06-01 12:04:07 +00:00
Andre601 2abde6ef63 Merge pull request 'Update dependency ch.qos.logback:logback-classic to v1.5.33' (#587) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/587
2026-05-28 14:27:41 +02:00
Andre601's Bot da2cf4d501 Update dependency ch.qos.logback:logback-classic to v1.5.33 2026-05-28 12:04:02 +00:00
Andre601 2a00634ce2 Merge pull request 'Update maven Docker tag to v3.9.16' (#586) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/586
2026-05-19 14:04:23 +02:00
Andre601's Bot 58108a8b19 Update maven Docker tag to v3.9.16 2026-05-19 12:03:28 +00:00
Andre601 6e7260d8f2 Bump adventure to 5.1.1 to avoid dependency errors 2026-05-12 17:59:48 +02:00
Andre601 d75f43a4bd Merge pull request 'Update all non-major dependencies to v5.1.0' (#585) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/585
2026-05-12 17:56:09 +02:00
Andre601's Bot 442c1539e9 Update all non-major dependencies to v5.1.0 2026-05-12 12:02:57 +00:00
Andre601 a6188905dc Merge pull request 'andre601-patch-1' (#584) from andre601-patch-1 into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/584
2026-05-07 11:05:09 +02:00
Andre601 81e0778825 Use version range 2026-05-07 11:03:27 +02:00
Andre601 afdcd11f7c Use version range 2026-05-07 11:02:46 +02:00
Andre601 4ca00cd8ad Merge pull request 'Update all non-major dependencies' (#583) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/583
2026-05-06 15:01:59 +02:00
Andre601's Bot 3b2d6b98b2 Update all non-major dependencies 2026-05-05 12:06:22 +00:00
Andre601 348d1fcacf v5.8.0
- Use 1.7.0-b1 of ExpressionParser
- Wrap condition evaluation in try-catch to prevent stacktrace spams for invalid protocols (bots?) pinging.
2026-05-03 21:39:48 +02:00
Andre601 4451d2382b Remove FakePlayerProfile and make hover only work on newer Paper versions 2026-05-01 21:53:52 +02:00
Andre601 3d410631ef Merge pull request 'Update dependency io.papermc.paper:paper-api to v26' (#582) from renovate/io.papermc.paper-paper-api-26.x into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/582
2026-05-01 14:07:15 +02:00
Andre601 e0ba710aed paper/pom.xml aktualisiert 2026-05-01 14:07:06 +02:00
Andre601's Bot 966d817f29 Update dependency io.papermc.paper:paper-api to v26 2026-05-01 12:05:19 +00:00
Andre601 838416b1c5 Merge pull request 'Update dependency org.junit.jupiter:junit-jupiter-engine to v5.14.4' (#581) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/581
2026-04-29 14:17:44 +02:00
Andre601's Bot 23ee4a08ad Update dependency org.junit.jupiter:junit-jupiter-engine to v5.14.4 2026-04-29 12:04:12 +00:00
Andre601 baba7c7076 Use git-pages action for docs deployment 2026-04-25 14:14:23 +02:00
Andre601 7e7228dd77 Merge pull request 'Update dependency com.google.code.gson:gson to v2.14.0' (#580) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/580
2026-04-25 14:09:40 +02:00
Andre601's Bot 103bf4f1db Update dependency com.google.code.gson:gson to v2.14.0 2026-04-25 12:04:19 +00:00
Andre601 0c7c8e56db Update default config in getting-started.md 2026-04-25 01:25:17 +02:00
Andre601 cb3161a118 Merge pull request 'Update dependency net.kyori:adventure-text-serializer-legacy to v5' (#579) from renovate/major-dependency.adventure-text-serializer-legacy into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/579
2026-04-22 14:11:44 +02:00
Andre601 326fe7098a Merge pull request 'Update all non-major dependencies' (#578) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/578
2026-04-22 14:11:25 +02:00
Andre601's Bot d79c54f7c9 Update dependency net.kyori:adventure-text-serializer-legacy to v5 2026-04-22 12:04:30 +00:00
Andre601's Bot 1468febf72 Update all non-major dependencies 2026-04-22 12:04:25 +00:00
Andre601 afbcad6c3e Merge pull request 'Update dependency net.kyori:adventure-text-minimessage to v5' (#577) from renovate/major-dependency.adventure-text-minimessage into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/577
2026-04-19 14:06:31 +02:00
Andre601 79aefaa6a5 Merge pull request 'Update dependency org.apache.httpcomponents.client5:httpclient5 to v5.6.1' (#576) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/576
2026-04-19 14:06:06 +02:00
Andre601's Bot d6ac483a0e Update dependency net.kyori:adventure-text-minimessage to v5 2026-04-19 12:05:15 +00:00
Andre601's Bot 35df0d84ca Update dependency org.apache.httpcomponents.client5:httpclient5 to v5.6.1 2026-04-19 12:04:58 +00:00
Andre601 45229bd6d2 Merge pull request 'Update dependency eu.kennytv.maintenance:maintenance-api to v5' (#575) from renovate/eu.kennytv.maintenance-maintenance-api-5.x into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/575
2026-04-18 14:14:07 +02:00
Andre601 7bfa88383d Merge pull request 'Update dependency net.kyori:adventure-api to v5.0.1' (#574) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/574
2026-04-18 14:12:28 +02:00
Andre601's Bot efaae58562 Update dependency eu.kennytv.maintenance:maintenance-api to v5 2026-04-18 12:07:08 +00:00
Andre601's Bot 7dfdbb621d Update dependency net.kyori:adventure-api to v5.0.1 2026-04-18 12:06:54 +00:00
Andre601 d2cd554fb1 Merge pull request 'Update dependency net.kyori:adventure-api to v5' (#573) from renovate/major-dependency.adventure-api into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/573
2026-04-16 15:50:12 +02:00
Andre601 69168efb01 Merge pull request 'Update dependency com.google.guava:guava to v33.6.0-jre' (#572) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/572
2026-04-16 15:49:56 +02:00
Andre601's Bot 55f94a8c27 Update dependency net.kyori:adventure-api to v5 2026-04-16 12:04:32 +00:00
Andre601's Bot e813bdaba4 Update dependency com.google.guava:guava to v33.6.0-jre 2026-04-16 12:04:13 +00:00
Andre601 b3f56aa398 Merge pull request 'Update all non-major dependencies to v2.0.0-beta.15' (#571) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/571
2026-04-12 14:43:39 +02:00
Andre601's Bot 87f0573162 Update all non-major dependencies to v2.0.0-beta.15 2026-04-12 12:04:33 +00:00
Andre601 e49fe391d0 Merge pull request 'Update dependency com.viaversion:viaversion-api to v5.8.1' (#570) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/570
2026-03-31 14:06:26 +02:00
Andre601's Bot d077efdc12 Update dependency com.viaversion:viaversion-api to v5.8.1 2026-03-31 12:04:41 +00:00
Andre601 90fdaf628c Merge pull request 'Update dependency mkdocs-redirects to v1.2.3' (#569) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/569
2026-03-29 14:17:30 +02:00
Andre601's Bot 9651011b19 Update dependency mkdocs-redirects to v1.2.3 2026-03-29 12:02:58 +00:00
Andre601 9bacc04a68 Compile with Java 25 2026-03-28 13:52:41 +01:00
Andre601 515081e395 Merge pull request 'Update all non-major dependencies' (#568) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/568
2026-03-28 13:07:37 +01:00
Andre601's Bot 95fec87c12 Update all non-major dependencies 2026-03-28 12:07:06 +00:00
Andre601 778a415b4f Merge pull request 'Update dependency properdocs to v1.6.7' (#567) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/567
2026-03-21 14:23:28 +01:00
Andre601's Bot db66945d99 Update dependency properdocs to v1.6.7 2026-03-21 12:02:41 +00:00
Andre601 cf4245842b Bump version 2026-03-20 20:37:39 +01:00
Andre601 d90084196b Only print Stacktrace in debug in FaviconHandler 2026-03-20 20:37:11 +01:00
Andre601 62b13a3cdf Use key from uuid and text for PAPI caching 2026-03-20 20:36:11 +01:00
Andre601 be6e5f8bfb Merge pull request 'Update dependency mkdocs-material to v9.7.6' (#566) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/566
2026-03-20 13:06:39 +01:00
Andre601's Bot aaf1439da8 Update dependency mkdocs-material to v9.7.6 2026-03-20 12:03:01 +00:00
Andre601 b304252140 Move to ProperDocs 2026-03-17 22:01:03 +01:00
Andre601 dda321f07b Add MiniPlaceholders info 2026-03-14 21:46:11 +01:00
Andre601 f487fd9cc1 Merge pull request 'Update maven Docker tag to v3.9.14' (#564) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/564
2026-03-13 13:04:03 +01:00
Andre601's Bot 5681e7a790 Update maven Docker tag to v3.9.14 2026-03-13 12:02:54 +00:00
Andre601 c3aa8bf536 Merge pull request 'Update dependency mkdocs-material to v9.7.5' (#563) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/563
2026-03-11 13:10:39 +01:00
Andre601's Bot 1ce27b2244 Update dependency mkdocs-material to v9.7.5 2026-03-11 12:02:59 +00:00
Andre601 74bce4c220 Merge pull request 'Update maven Docker tag to v3.9.13' (#562) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/562
2026-03-10 13:06:08 +01:00
Andre601's Bot 6605538b0f Update maven Docker tag to v3.9.13 2026-03-10 12:03:30 +00:00
Andre601 4aa9e946fd Merge pull request 'Update dependency org.apache.maven.plugins:maven-shade-plugin to v3.6.2' (#560) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/560
2026-03-05 13:06:17 +01:00
Andre601's Bot 42ec17997e Update dependency org.apache.maven.plugins:maven-shade-plugin to v3.6.2 2026-03-05 12:03:02 +00:00
Andre601 d2587e67f4 Merge pull request 'Update dependency mkdocs-material to v9.7.4' (#559) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/559
2026-03-04 13:12:17 +01:00
Andre601's Bot 3f8f0e0ff7 Update dependency mkdocs-material to v9.7.4 2026-03-04 12:02:13 +00:00
Andre601 b0dbbbfe82 Merge pull request 'Update dependency com.viaversion:viaversion-api to v5.7.2' (#558) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/558
2026-03-02 13:31:12 +01:00
Andre601's Bot 6a32ea7826 Update dependency com.viaversion:viaversion-api to v5.7.2 2026-03-02 12:02:40 +00:00
Andre601 a6bcc7036c Merge pull request 'Update all non-major dependencies' (#557) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/557
2026-02-25 13:20:20 +01:00
Andre601's Bot 639b32db23 Update all non-major dependencies 2026-02-25 12:02:18 +00:00
Andre601 53672243d3 Merge pull request 'Update all non-major dependencies to v3.2.0' (#556) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/556
2026-02-24 13:07:49 +01:00
Andre601's Bot 3b7e177e4f Update all non-major dependencies to v3.2.0 2026-02-24 12:02:50 +00:00
Andre601 6b43ef4dc3 Merge pull request 'Update dependency mkdocs-material to v9.7.2' (#555) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/555
2026-02-19 13:09:00 +01:00
Andre601's Bot 665990aae9 Update dependency mkdocs-material to v9.7.2 2026-02-19 12:02:36 +00:00
Andre601 70723ce35e Merge pull request 'Update all non-major dependencies' (#554) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/554
2026-02-16 13:04:30 +01:00
Andre601's Bot b2d689306b Update all non-major dependencies 2026-02-16 12:02:58 +00:00
Andre601 6ae3f0aea6 Merge pull request 'Update dependency ch.qos.logback:logback-classic to v1.5.31' (#552) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/552
2026-02-15 13:07:43 +01:00
Andre601's Bot 9445e12f8d Update dependency ch.qos.logback:logback-classic to v1.5.31 2026-02-15 12:03:57 +00:00
Andre601 94da2bbd2d Merge pull request 'Update dependency ch.qos.logback:logback-classic to v1.5.30' (#551) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/551
2026-02-14 13:06:08 +01:00
Andre601's Bot b5d0246bce Update dependency ch.qos.logback:logback-classic to v1.5.30 2026-02-14 12:03:00 +00:00
Andre601 48b1e53d13 Merge pull request 'Update dependency ch.qos.logback:logback-classic to v1.5.29' (#550) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/550
2026-02-10 13:09:07 +01:00
Andre601's Bot 5e7a59474d Update dependency ch.qos.logback:logback-classic to v1.5.29 2026-02-10 12:03:00 +00:00
Andre601 bfbc547712 Merge pull request 'Update dependency me.clip:placeholderapi to v2.12.2' (#549) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/549
2026-02-09 13:08:33 +01:00
Andre601's Bot 2f8273d6a3 Update dependency me.clip:placeholderapi to v2.12.2 2026-02-09 12:03:44 +00:00
Andre601 1f036727d8 Merge pull request 'Update dependency ch.qos.logback:logback-classic to v1.5.28' (#548) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/548
2026-02-07 13:15:45 +01:00
Andre601's Bot bc07dadf03 Update dependency ch.qos.logback:logback-classic to v1.5.28 2026-02-07 12:04:00 +00:00
Andre601 ac0c754050 Merge pull request 'Update dependency me.clip:placeholderapi to v2.12.1' (#547) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/547
2026-02-03 13:11:45 +01:00
Andre601's Bot b2d664b5b9 Update dependency me.clip:placeholderapi to v2.12.1 2026-02-03 12:03:33 +00:00
Andre601 58e9e55dc6 Use AlessioDP snapshot repository 2026-02-02 14:44:30 +01:00
Andre601 9b8e459b5c Merge pull request 'Update all non-major dependencies' (#546) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/546
2026-02-02 14:20:05 +01:00
Andre601's Bot 598343ebc7 Update all non-major dependencies 2026-02-02 12:03:54 +00:00
Andre601 c1fd983dab Merge pull request 'Update dependency ch.qos.logback:logback-classic to v1.5.27' (#545) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/545
2026-01-31 13:07:58 +01:00
Andre601's Bot d05faf30dc Update dependency ch.qos.logback:logback-classic to v1.5.27 2026-01-31 12:04:24 +00:00
Andre601 79a96f30ca Merge pull request 'Update all non-major dependencies' (#544) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/544
2026-01-26 14:01:04 +01:00
Andre601's Bot 54ddf4bb4d Update all non-major dependencies 2026-01-26 12:02:48 +00:00
Andre601 f8b85ce9ed Merge pull request 'Update dependency com.viaversion:viaversion-api to v5.7.1' (#543) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/543
2026-01-19 13:38:34 +01:00
Andre601's Bot 2f5c1db3c1 Update dependency com.viaversion:viaversion-api to v5.7.1 2026-01-19 12:02:20 +00:00
Andre601 de7570ac6e Merge pull request 'Update dependency ch.qos.logback:logback-classic to v1.5.25' (#542) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/542
2026-01-18 14:08:29 +01:00
Andre601's Bot 41c7db5f27 Update dependency ch.qos.logback:logback-classic to v1.5.25 2026-01-18 12:03:56 +00:00
Andre601 ef0ef4e373 Merge pull request 'Update all non-major dependencies' (#539) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/539
2026-01-07 13:27:03 +01:00
Andre601's Bot e9ab05618e Update all non-major dependencies 2026-01-07 12:04:09 +00:00
Andre601 a28e62f53c Bump version to 5.7.2 2026-01-02 20:13:08 +01:00
Andre601 e82df2d1f7 Merge pull request 'fix date pattern argument not working with date placeholders' (#538) from UninenVille/AdvancedServerList:fix-date-pattern-argument into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/538
2026-01-02 20:10:17 +01:00
UninenVille 16a2b05b2b fix date pattern argument not working with date placeholders
Date pattern argument is third argument, not second
2026-01-02 19:43:12 +01:00
Andre601 5df3d6dd48 Add missing description 2026-01-02 15:54:51 +01:00
Andre601 91a6149dac Add LuckPerms addon to getting started page 2026-01-02 15:52:42 +01:00
Andre601 3f224504a1 Bump version to 5.7.1 2026-01-01 20:36:18 +01:00
Andre601 437e155a46 Fix NPE for '/asl profiles set' command due to no CommandType meta being added. 2026-01-01 17:59:11 +01:00
Andre601 8275069315 Merge pull request 'Update dependency com.viaversion:viaversion-api to v5.7.0' (#536) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/536
2025-12-30 13:07:38 +01:00
Andre601's Bot bd564a8638 Update dependency com.viaversion:viaversion-api to v5.7.0 2025-12-30 12:02:26 +00:00
Andre601 591279f70b Merge pull request 'Update dependency ch.qos.logback:logback-classic to v1.5.23' (#535) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/535
2025-12-22 13:15:18 +01:00
Andre601's Bot 80e84aa41b Update dependency ch.qos.logback:logback-classic to v1.5.23 2025-12-22 12:11:31 +00:00
Andre601 9024c4dc71 Merge pull request 'Update all non-major dependencies to v1.7.3' (#534) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/534
2025-12-21 13:10:53 +01:00
Andre601's Bot bb0f389016 Update all non-major dependencies to v1.7.3 2025-12-21 12:02:21 +00:00
Andre601 f8f8a45b1a Merge pull request 'Update all non-major dependencies' (#533) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/533
2025-12-20 13:54:48 +01:00
Andre601's Bot 0bae24bb66 Update all non-major dependencies 2025-12-20 12:02:32 +00:00
Andre601 2158caebe6 Fix bad cloud versions 2025-12-19 22:24:04 +01:00
Andre601 f9ea253da3 Add MiniPlaceholders as optional dependency 2025-12-19 22:06:03 +01:00
Andre601 7c53ddf4f4 Fix wrong value for dateFormat.parse 2025-12-19 21:56:16 +01:00
Andre601 8bcc88d620 Bump version to 5.7.0 2025-12-19 21:38:17 +01:00
Andre601 78b687587d Merge pull request 'Add bluesky Post step to publish_assets.yml' (#385) from feature/bluesky-poster into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/385
2025-12-19 21:36:53 +01:00
Andre601 168fe364b0 Looooooots of changes 2025-12-19 21:18:31 +01:00
Andre601 cd3f54dba6 Merge pull request 'Update all non-major dependencies' (#532) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/532
2025-12-18 13:08:30 +01:00
Andre601's Bot 79f15b2467 Update all non-major dependencies 2025-12-18 12:03:21 +00:00
Andre601 ade8ad5a33 Merge pull request 'Update all non-major dependencies' (#531) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/531
2025-12-14 21:25:07 +01:00
Andre601's Bot 6e343f1516 Update all non-major dependencies 2025-12-13 12:03:22 +00:00
Andre601 fee436fda6 Update links in default.yml 2025-12-09 23:59:37 +01:00
Andre601 27d4f5acd0 Merge pull request 'Update https://code.forgejo.org/actions/checkout action to v6' (#527) from renovate/https-code.forgejo.org-actions-checkout-6.x into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/527
2025-11-21 13:16:03 +01:00
Andre601's Bot e18c70d446 Update https://code.forgejo.org/actions/checkout action to v6 2025-11-21 12:02:31 +00:00
Andre601 0ca3ec62a9 Merge pull request 'Update dependency com.squareup.okhttp3:okhttp-jvm to v5.3.2' (#526) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/526
2025-11-19 13:21:06 +01:00
Andre601's Bot 461ae5d0aa Update dependency com.squareup.okhttp3:okhttp-jvm to v5.3.2 2025-11-19 12:06:53 +00:00
Andre601 5696a9417b Merge pull request 'Update dependency com.squareup.okhttp3:okhttp-jvm to v5.3.1' (#523) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/523
2025-11-17 13:16:39 +01:00
Andre601's Bot 0705101cd6 Update dependency com.squareup.okhttp3:okhttp-jvm to v5.3.1 2025-11-17 12:04:39 +00:00
Andre601 69ad3554cf Merge pull request 'Update dependency org.apache.maven.plugins:maven-jar-plugin to v3.5.0' (#522) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/522
2025-11-15 13:46:38 +01:00
Andre601's Bot 7f1de13dcc Update dependency org.apache.maven.plugins:maven-jar-plugin to v3.5.0 2025-11-15 12:02:40 +00:00
Andre601 c3e16eec31 Merge pull request 'Update dependency mkdocs-macros-plugin to v1.5.0' (#521) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/521
2025-11-14 13:06:21 +01:00
Andre601's Bot 207970a90b Update dependency mkdocs-macros-plugin to v1.5.0 2025-11-14 12:02:26 +00:00
Andre601 2628dbab56 Merge pull request 'Update all non-major dependencies' (#520) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/520
2025-11-11 13:13:53 +01:00
Andre601's Bot 3da9f8b400 Update all non-major dependencies 2025-11-11 12:02:48 +00:00
Andre601 ffd0a8c364 Merge pull request 'Update all non-major dependencies to v1.7.2' (#519) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/519
2025-11-06 15:17:34 +01:00
Andre601's Bot d9a7b244f9 Update all non-major dependencies to v1.7.2 2025-11-06 14:01:26 +00:00
Andre601 f1fb5ef6ea Merge pull request 'Update dependency me.clip:placeholderapi to v2.11.7' (#518) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/518
2025-11-03 13:23:37 +01:00
Andre601's Bot d3406aad1c Update dependency me.clip:placeholderapi to v2.11.7 2025-11-03 12:02:22 +00:00
Andre601 650bcc3235 Merge pull request 'Update dependency mkdocs-material to v9.6.23' (#517) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/517
2025-11-02 13:25:54 +01:00
Andre601's Bot 0cd91c63bb Update dependency mkdocs-material to v9.6.23 2025-11-02 12:02:33 +00:00
Andre601 e95a4634ec Merge pull request 'Update dependency org.junit.jupiter:junit-jupiter-engine to v5.14.1' (#516) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/516
2025-11-01 13:03:15 +01:00
Andre601's Bot d0293ae15e Update dependency org.junit.jupiter:junit-jupiter-engine to v5.14.1 2025-11-01 12:02:21 +00:00
Andre601 f5c6185b96 Merge pull request 'Update dependency com.squareup.okhttp3:okhttp-jvm to v5.3.0' (#515) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/515
2025-10-31 13:10:21 +01:00
Andre601's Bot 61981db3cc Update dependency com.squareup.okhttp3:okhttp-jvm to v5.3.0 2025-10-31 12:03:05 +00:00
Andre601 52804adfdf Merge pull request 'Update all non-major dependencies to v1.7.1' (#514) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/514
2025-10-28 13:06:10 +01:00
Andre601's Bot 4b185be8a4 Update all non-major dependencies to v1.7.1 2025-10-28 12:03:01 +00:00
Andre601 46ac4ef5c1 Merge pull request 'Update dependency mkdocs-macros-plugin to v1.4.1' (#513) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/513
2025-10-26 13:44:51 +01:00
Andre601's Bot 00e2a8ebee Update dependency mkdocs-macros-plugin to v1.4.1 2025-10-26 12:03:57 +00:00
Andre601 c523da7477 Merge pull request 'Update dependency mkdocs-glightbox to v0.5.2' (#512) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/512
2025-10-24 14:06:11 +02:00
Andre601's Bot 01e586fa5e Update dependency mkdocs-glightbox to v0.5.2 2025-10-24 12:04:10 +00:00
Andre601 9322819538 Merge pull request 'Update dependency ch.qos.logback:logback-classic to v1.5.20' (#511) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/511
2025-10-20 14:37:17 +02:00
Andre601's Bot 7ef4a9a61b Update dependency ch.qos.logback:logback-classic to v1.5.20 2025-10-20 12:02:54 +00:00
Andre601 af2da61500 Merge pull request 'Update dependency mkdocs-material to v9.6.22' (#510) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/510
2025-10-15 14:05:25 +02:00
Andre601's Bot 23d0626250 Update dependency mkdocs-material to v9.6.22 2025-10-15 12:02:21 +00:00
Andre601 74dfdb3022 Add profiles edit command 2025-10-14 03:07:19 +02:00
Andre601 4a589619bd Merge pull request 'Update dependency com.viaversion:viaversion-api to v5.5.1' (#509) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/509
2025-10-13 15:19:37 +02:00
Andre601's Bot 9bdd519246 Update dependency com.viaversion:viaversion-api to v5.5.1 2025-10-13 12:05:43 +00:00
Andre601 cf4e08fe3e Merge pull request 'Update dependency org.incendo:cloud-minecraft-extras to v2.0.0-beta.13' (#508) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/508
2025-10-12 14:11:01 +02:00
Andre601's Bot 70bbb15c60 Update dependency org.incendo:cloud-minecraft-extras to v2.0.0-beta.13 2025-10-12 12:02:33 +00:00
Andre601 ee8e00f3a2 Merge pull request 'Update dependency com.squareup.okhttp3:okhttp-jvm to v5.2.1' (#507) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/507
2025-10-10 14:37:51 +02:00
Andre601's Bot 0280668f9b Update dependency com.squareup.okhttp3:okhttp-jvm to v5.2.1 2025-10-10 12:07:09 +00:00
Andre601 e959f478a0 Merge pull request 'Update dependency com.squareup.okhttp3:okhttp-jvm to v5.2.0' (#505) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/505
2025-10-08 14:03:52 +02:00
Andre601's Bot f024b2c763 Update dependency com.squareup.okhttp3:okhttp-jvm to v5.2.0 2025-10-08 12:02:40 +00:00
Andre601 c0665b450e Merge pull request 'Update all non-major dependencies' (#504) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/504
2025-10-07 15:25:22 +02:00
Andre601's Bot e50ed552b9 Update all non-major dependencies 2025-10-07 12:02:48 +00:00
Andre601 1601e41e9c Merge pull request 'Update https://github.com/peter-evans/create-or-update-comment action to v5' (#503) from renovate/https-github.com-peter-evans-create-or-update-comment-5.x into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/503
2025-10-03 14:04:33 +02:00
Andre601 f8b80f9e14 Merge pull request 'Update dependency com.viaversion:viaversion-api to v5.5.0' (#502) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/502
2025-10-03 14:03:52 +02:00
Andre601's Bot 22bfe2be00 Update https://github.com/peter-evans/create-or-update-comment action to v5 2025-10-03 12:02:31 +00:00
Andre601's Bot 7e9c6b909c Update dependency com.viaversion:viaversion-api to v5.5.0 2025-10-03 12:02:26 +00:00
Andre601 93ecdd736f Merge pull request 'Bump checkout action to v5' (#501) from feat/update-checkout-action into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/501
2025-10-02 15:54:08 +02:00
Andre601 abfbaf4498 Bump checkout action to v5 2025-10-02 15:53:12 +02:00
Andre601 ce3b80b05c Bump checkout action to v5 2025-10-02 15:52:46 +02:00
Andre601 9e3a77ad7d Merge pull request 'Update https://github.com/peter-evans/find-comment action to v4' (#500) from renovate/https-github.com-peter-evans-find-comment-4.x into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/500
2025-10-02 14:28:01 +02:00
Andre601's Bot cdc2115cb3 Update https://github.com/peter-evans/find-comment action to v4 2025-10-02 12:02:16 +00:00
Andre601 5173f61c6c Merge pull request 'Update all non-major dependencies' (#499) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/499
2025-10-01 14:09:07 +02:00
Andre601's Bot 8b12cb2402 Update all non-major dependencies 2025-10-01 12:02:30 +00:00
Andre601 3ab48396af Merge pull request 'Update dependency org.junit.jupiter:junit-jupiter-engine to v5.14.0' (#497) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/497
2025-09-30 14:56:02 +02:00
Andre601's Bot 00b34c8b47 Update dependency org.junit.jupiter:junit-jupiter-engine to v5.14.0 2025-09-30 12:04:24 +00:00
Andre601 b06274638e Merge pull request 'Update dependency org.apache.httpcomponents.client5:httpclient5 to v5.5.1' (#496) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/496
2025-09-28 14:13:04 +02:00
Andre601's Bot cd3b2fe0b4 Update dependency org.apache.httpcomponents.client5:httpclient5 to v5.5.1 2025-09-28 12:02:24 +00:00
Andre601 51d8ae6c42 Merge pull request 'Update dependency net.md-5:bungeecord-api to v1.21-R0.4' (#495) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/495
2025-09-27 14:07:19 +02:00
Andre601's Bot 1545be4709 Update dependency net.md-5:bungeecord-api to v1.21-R0.4 2025-09-27 12:02:43 +00:00
Andre601 7693e72728 Merge pull request 'Update dependency mkdocs-macros-plugin to v1.4.0' (#494) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/494
2025-09-23 14:07:14 +02:00
Andre601's Bot b879110379 Update dependency mkdocs-macros-plugin to v1.4.0 2025-09-23 12:02:24 +00:00
Andre601 80900089bb Merge pull request 'Update dependency org.apache.maven.plugins:maven-compiler-plugin to v3.14.1' (#493) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/493
2025-09-22 14:04:44 +02:00
Andre601's Bot df776f4e1c Update dependency org.apache.maven.plugins:maven-compiler-plugin to v3.14.1 2025-09-22 12:02:19 +00:00
Andre601 72afff8315 Merge pull request 'Update dependency com.google.guava:guava to v33.5.0-jre' (#492) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/492
2025-09-18 14:10:37 +02:00
Andre601's Bot 307384d1e1 Update dependency com.google.guava:guava to v33.5.0-jre 2025-09-18 12:03:10 +00:00
Andre601 84f31fad53 Merge pull request 'Update dependency mkdocs-material to v9.6.20' (#490) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/490
2025-09-15 14:36:25 +02:00
Andre601's Bot fe0774303a Update dependency mkdocs-material to v9.6.20 2025-09-15 12:05:20 +00:00
Andre601 563173679b Merge pull request 'Update dependency org.apache.maven.plugins:maven-shade-plugin to v3.6.1' (#489) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/489
2025-09-13 14:14:06 +02:00
Andre601's Bot 6e96e06109 Update dependency org.apache.maven.plugins:maven-shade-plugin to v3.6.1 2025-09-13 12:02:50 +00:00
Andre601 dae25fd00a Merge pull request 'Update dependency space.arim.libertybans:bans-api to v1.1.2' (#488) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/488
2025-09-12 14:43:02 +02:00
Andre601's Bot d2ca0cfae5 Update dependency space.arim.libertybans:bans-api to v1.1.2 2025-09-12 12:02:50 +00:00
Andre601 afaae368ac Merge pull request 'Update dependency com.google.code.gson:gson to v2.13.2' (#487) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/487
2025-09-11 15:00:19 +02:00
Andre601's Bot 522ce7f932 Update dependency com.google.code.gson:gson to v2.13.2 2025-09-11 12:02:07 +00:00
Andre601 3f3d460f2f Merge pull request 'Update dependency mkdocs-material to v9.6.19' (#486) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/486
2025-09-08 14:06:06 +02:00
Andre601's Bot 3a03899688 Update dependency mkdocs-material to v9.6.19 2025-09-08 12:02:12 +00:00
Andre601 e6d4264525 Merge pull request 'Update dependency mkdocs-glightbox to v0.5.1' (#485) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/485
2025-09-05 14:37:00 +02:00
Andre601's Bot 61466074fd Update dependency mkdocs-glightbox to v0.5.1 2025-09-05 12:02:34 +00:00
Andre601 4516157ddf Merge pull request 'Update https://code.forgejo.org/actions/setup-python action to v6' (#483) from renovate/https-code.forgejo.org-actions-setup-python-6.x into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/483
2025-09-04 15:42:23 +02:00
Andre601 82c65bf13c Merge pull request 'Update dependency mkdocs-glightbox to v0.5.0' (#482) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/482
2025-09-04 15:41:29 +02:00
Andre601's Bot 3c6e73671d Update https://code.forgejo.org/actions/setup-python action to v6 2025-09-04 12:02:27 +00:00
Andre601's Bot c91a2c0d4e Update dependency mkdocs-glightbox to v0.5.0 2025-09-04 12:02:07 +00:00
Andre601 66b10656c3 Update PlaceholderAPI link 2025-09-03 21:53:10 +02:00
Andre601 b82a0ff0d9 Update PlaceholderAPI link 2025-09-03 21:48:17 +02:00
Andre601 2e02af5a67 Merge pull request 'Update dependency mkdocs-material to v9.6.18' (#480) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/480
2025-08-22 14:32:26 +02:00
Andre601's Bot 5626b38390 Update dependency mkdocs-material to v9.6.18 2025-08-22 12:03:34 +00:00
Andre601 415456447c Merge pull request 'Update dependency mkdocs-material to v9.6.17' (#479) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/479
2025-08-16 16:43:33 +02:00
Andre601's Bot c555a3c648 Update dependency mkdocs-material to v9.6.17 2025-08-16 12:02:07 +00:00
Andre601 9ec0c1c4ce Merge pull request 'Update dependency mkdocs-macros-plugin to v1.3.9' (#477) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/477
2025-08-13 20:07:50 +02:00
Andre601 d3eb43db39 Revert Checkout to v4 2025-08-13 20:07:45 +02:00
Andre601 b4b4f6a9fc Merge branch 'master' into renovate/all-minor-patch 2025-08-13 20:03:53 +02:00
Andre601 3217eee02b Revert Checkout to v4 2025-08-13 20:02:54 +02:00
Andre601 ea68fca70b Merge branch 'master' into renovate/all-minor-patch 2025-08-13 20:01:08 +02:00
Andre601 a8ceb88b67 Fix missing env in RUN_URL 2025-08-13 20:00:56 +02:00
Andre601's Bot 19d6ce28c4 Update dependency mkdocs-macros-plugin to v1.3.9 2025-08-13 12:02:11 +00:00
Andre601 05e61794a1 Merge pull request 'Update https://code.forgejo.org/actions/checkout action to v5' (#476) from renovate/https-code.forgejo.org-actions-checkout-5.x into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/476
2025-08-12 15:30:45 +02:00
Andre601's Bot 46e062eb8f Update https://code.forgejo.org/actions/checkout action to v5 2025-08-12 12:02:44 +00:00
Andre601 2196456dd4 Rename faviconStrategy to faviconResizeStrategy for clarity 2025-08-10 13:39:51 +02:00
Andre601 bfd1e2e2ad Add note about updating local favicons 2025-08-10 13:29:30 +02:00
Andre601 21af6df510 Merge pull request 'Update all non-major dependencies to v4.24.0' (#475) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/475
2025-07-31 14:18:42 +02:00
Andre601's Bot 713062d1b4 Update all non-major dependencies to v4.24.0 2025-07-31 12:03:06 +00:00
Andre601 673a134ab1 Merge pull request 'Update Docs banner' (#474) from feature/update-banner into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/474
2025-07-30 23:17:07 +02:00
Andre601 c777772a90 ❇️ Sparke up the banner ❇️ 2025-07-30 23:05:14 +02:00
Andre601 a43a9f8f19 docs/.overrides/main.html aktualisiert 2025-07-30 22:10:52 +02:00
Andre601 d4a416f85e Merge pull request 'Update dependency net.kyori:adventure-platform-bungeecord to v4.4.1' (#473) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/473
2025-07-30 14:05:15 +02:00
Andre601's Bot bc26ceae55 Update dependency net.kyori:adventure-platform-bungeecord to v4.4.1 2025-07-30 12:02:25 +00:00
Andre601 3d19a6688c Merge pull request 'Update dependency mkdocs-material to v9.6.16' (#472) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/472
2025-07-27 14:32:57 +02:00
Andre601's Bot 03a8258ddc Update dependency mkdocs-material to v9.6.16 2025-07-27 12:03:25 +00:00
Andre601 6df5015b0d Merge pull request 'Update dependency com.viaversion:viaversion-api to v5.4.2' (#471) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/471
2025-07-24 15:42:01 +02:00
Andre601's Bot f1f8aaaad2 Update dependency com.viaversion:viaversion-api to v5.4.2 2025-07-24 12:02:08 +00:00
Andre601 151f5494fc Bumb version to 5.7.0-b2 2025-07-22 23:40:13 +02:00
Andre601 f997fa0a89 Use interfaces instead of Abstract classes 2025-07-22 23:39:16 +02:00
Andre601 2f1549eb8a Merge pull request 'Fix Dialog Support on Paper' (#468) from fix/paper-dialog-support into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/468
2025-07-22 23:27:47 +02:00
Andre601 77dba75c17 Use callback instead of event 2025-07-21 15:38:57 +02:00
Andre601 dfa72cb852 Merge pull request 'Update dependency org.junit.jupiter:junit-jupiter-engine to v5.13.4' (#469) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/469
2025-07-21 14:05:12 +02:00
Andre601's Bot dc0cf6ebfc Update dependency org.junit.jupiter:junit-jupiter-engine to v5.13.4 2025-07-21 12:02:16 +00:00
Andre601 2c520e53c0 Fix Dialog Support on Paper 2025-07-20 23:42:16 +02:00
Andre601 356cca9ba0 Merge pull request 'Update maven Docker tag to v3.9.11' (#467) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/467
2025-07-18 18:43:57 +02:00
Andre601's Bot 4e7b17a601 Update maven Docker tag to v3.9.11 2025-07-18 12:04:19 +00:00
Andre601 033f4bd6ab Make it a beta 2025-07-16 20:54:36 +02:00
Andre601 13edca3c4c Bump version 2025-07-16 20:53:56 +02:00
Andre601 a626dc5b28 Merge pull request 'Implement dialog-based profiles editing.' (#466) from feature/dialog-based-editing into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/466
2025-07-16 20:52:03 +02:00
Andre601 7a60024bd0 Merge branch 'master' into feature/dialog-based-editing 2025-07-16 20:51:51 +02:00
Andre601 cedb1e5748 Update Version uploader 2025-07-16 20:51:22 +02:00
Andre601 8b0fb99bc5 Use CommandPostprocessor for CommandType handling 2025-07-16 20:46:27 +02:00
Andre601 31202a930a Change cancel to a Back button. 2025-07-13 23:17:26 +02:00
Andre601 a105e3186b Create Builder class for Dialog building 2025-07-13 20:33:38 +02:00
Andre601 e2df79c428 Implement dialog-based profiles editing. 2025-07-12 22:23:36 +02:00
Andre601 01ecce170c Merge pull request 'Update dependency org.incendo:cloud-minecraft-extras to v2.0.0-beta.11' (#465) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/465
2025-07-12 14:04:26 +02:00
Andre601's Bot 3bb171cecf Update dependency org.incendo:cloud-minecraft-extras to v2.0.0-beta.11 2025-07-12 12:02:09 +00:00
Andre601 f54bbe26e1 Merge pull request 'Update dependency net.william278:papiproxybridge to v1.8.4' (#464) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/464
2025-07-09 14:16:53 +02:00
Andre601's Bot ddede7abef Update dependency net.william278:papiproxybridge to v1.8.4 2025-07-09 12:02:15 +00:00
Andre601 2c6467f176 Merge pull request 'Update dependency com.squareup.okhttp3:okhttp-jvm to v5.1.0' (#463) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/463
2025-07-08 14:05:49 +02:00
Andre601's Bot 8b92f8fc4d Update dependency com.squareup.okhttp3:okhttp-jvm to v5.1.0 2025-07-08 12:02:15 +00:00
Andre601 7bf6d8de0f Merge pull request 'Update dependency net.william278:papiproxybridge to v1.8.3' (#462) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/462
2025-07-06 14:10:34 +02:00
Andre601's Bot 844a88d381 Update dependency net.william278:papiproxybridge to v1.8.3 2025-07-06 12:02:23 +00:00
Andre601 fcea4e90e3 Merge pull request 'Update all non-major dependencies' (#461) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/461
2025-07-05 16:26:05 +02:00
Andre601's Bot 6a97debdf7 Update all non-major dependencies 2025-07-05 12:03:08 +00:00
Andre601 07fa6cf0ec Fix OkHttp version change + method renamings 2025-07-05 00:21:24 +02:00
Andre601 367f0f3501 Merge pull request 'Update dependency com.squareup.okhttp3:okhttp to v5' (#460) from renovate/major-okhttp-monorepo into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/460
2025-07-04 14:06:52 +02:00
Andre601 16dcaef825 Merge branch 'master' into renovate/major-okhttp-monorepo 2025-07-04 14:06:38 +02:00
Andre601 f780372fda Merge pull request 'Update dependency space.arim.libertybans:bans-api to v1.1.1' (#459) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/459
2025-07-04 14:06:13 +02:00
Andre601's Bot a8f8c0d770 Update dependency com.squareup.okhttp3:okhttp to v5 2025-07-04 12:02:29 +00:00
Andre601's Bot 70e06afaf0 Update dependency space.arim.libertybans:bans-api to v1.1.1 2025-07-04 12:02:20 +00:00
Andre601 13e087eddd Merge pull request 'Update all non-major dependencies' (#458) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/458
2025-07-01 14:04:14 +02:00
Andre601's Bot 31af916f04 Update all non-major dependencies 2025-07-01 12:02:18 +00:00
Andre601 0c36667116 Version 5.6.0 2025-06-25 20:44:01 +02:00
Andre601 3417d7c0be Merge pull request 'Update dependency org.junit.jupiter:junit-jupiter-engine to v5.13.2' (#456) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/456
2025-06-25 14:05:24 +02:00
Andre601's Bot 96fa871204 Update dependency org.junit.jupiter:junit-jupiter-engine to v5.13.2 2025-06-25 12:02:49 +00:00
Andre601 aaacd9944f Merge pull request 'Update maven Docker tag to v3.9.10' (#455) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/455
2025-06-24 14:35:21 +02:00
Andre601's Bot af44324cf1 Update maven Docker tag to v3.9.10 2025-06-24 12:02:49 +00:00
Andre601 77b6897bd4 Add note about favicon resizing 2025-06-23 23:21:02 +02:00
Andre601 9d963199ae Merge pull request 'Update dependency com.viaversion:viaversion-api to v5.4.0' (#454) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/454
2025-06-21 14:03:06 +02:00
Andre601's Bot 580b87ebb5 Update dependency com.viaversion:viaversion-api to v5.4.0 2025-06-21 12:02:16 +00:00
Andre601 efd5762162 Update README 2025-06-19 23:55:08 +02:00
Andre601 62261ae6ed Update config.yml example in getting-started 2025-06-19 21:29:06 +02:00
Andre601 e52bfdf60b Add faviconStrategy config option 2025-06-19 21:18:41 +02:00
Andre601 0152ba3e1a Update default.yml in getting-started.md 2025-06-19 17:04:55 +02:00
Andre601 090787e292 Add german tutorial 2025-06-19 16:40:55 +02:00
Andre601 25d14eeaae Add indonesian tutorial 2025-06-19 16:32:57 +02:00
Andre601 386d490596 Merge pull request 'Update all non-major dependencies to v4.23.0' (#453) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/453
2025-06-18 20:56:11 +02:00
Andre601's Bot 207ce12452 Update all non-major dependencies to v4.23.0 2025-06-18 18:48:21 +00:00
Andre601 b87703c36c Merge pull request 'Update dependency net.md-5:bungeecord-api to v1.21-R0.3' (#452) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/452
2025-06-16 14:05:20 +02:00
Andre601's Bot 41677d0635 Update dependency net.md-5:bungeecord-api to v1.21-R0.3 2025-06-16 12:02:50 +00:00
Andre601 dfdb2aa767 Merge pull request 'Update dependency org.junit.jupiter:junit-jupiter-engine to v5.13.1' (#451) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/451
2025-06-07 14:58:29 +02:00
Andre601's Bot d14c5f80e8 Update dependency org.junit.jupiter:junit-jupiter-engine to v5.13.1 2025-06-07 12:02:12 +00:00
Andre601 bd9e376d08 Merge pull request 'Implement support for merging images' (#450) from feature/favicon-merging into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/450
2025-06-03 14:54:15 +02:00
Andre601 578242078b Move local and random Favicon handling to the start 2025-06-03 14:33:36 +02:00
Andre601 f84842729d Update documentation 2025-06-03 01:04:39 +02:00
Andre601 1daeecbbcf Create proper Favicon merging system 2025-06-03 00:56:37 +02:00
Andre601 5dcab6c37c Merge pull request 'Update dependency org.junit.jupiter:junit-jupiter-engine to v5.13.0' (#449) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/449
2025-05-30 14:02:34 +02:00
Andre601's Bot a146a10d53 Update dependency org.junit.jupiter:junit-jupiter-engine to v5.13.0 2025-05-30 12:02:12 +00:00
Andre601 75db0d5e96 Add Favicon Merging 2025-05-28 20:17:40 +02:00
Andre601 644dd401d4 Update profiles option page 2025-05-28 19:58:14 +02:00
Andre601 32d85c6be1 Merge pull request 'Update dependency org.apache.httpcomponents.client5:httpclient5 to v5.5' (#448) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/448
2025-05-23 14:04:19 +02:00
Andre601's Bot 1b47af6714 Update dependency org.apache.httpcomponents.client5:httpclient5 to v5.5 2025-05-23 12:03:00 +00:00
Andre601 4544696a54 Merge pull request 'Update dependency mkdocs-material to v9.6.14' (#447) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/447
2025-05-14 12:23:10 +00:00
Andre601's Bot a84848daee Update dependency mkdocs-material to v9.6.14 2025-05-14 12:03:01 +00:00
Andre601 3f8dd90e28 Merge pull request 'Update dependency net.kyori:adventure-platform-bungeecord to v4.4.0' (#446) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/446
2025-05-11 15:40:33 +00:00
Andre601's Bot f633741988 Update dependency net.kyori:adventure-platform-bungeecord to v4.4.0 2025-05-11 12:02:21 +00:00
Andre601 7e9ea9f470 Merge pull request 'Update dependency mkdocs-material to v9.6.13' (#445) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/445
2025-05-10 12:14:35 +00:00
Andre601's Bot c2c00dc47f Update dependency mkdocs-material to v9.6.13 2025-05-10 12:02:52 +00:00
Andre601 027503efab Merge pull request 'Update dependency net.william278:papiproxybridge to v1.8.1' (#444) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/444
2025-05-06 12:31:54 +00:00
Andre601's Bot 7dd78cbbfc Update dependency net.william278:papiproxybridge to v1.8.1 2025-05-06 12:03:06 +00:00
Andre601 b27b8e78bd Merge pull request 'Update all non-major dependencies to v4.21.0' (#443) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/443
2025-04-30 12:13:12 +00:00
Andre601's Bot ad5b07feda Update all non-major dependencies to v4.21.0 2025-04-30 12:03:04 +00:00
Andre601 d79784d1c0 Merge pull request 'Update all non-major dependencies to v4.1.1' (#442) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/442
2025-04-29 12:06:11 +00:00
Andre601's Bot ef84e85426 Update all non-major dependencies to v4.1.1 2025-04-29 12:02:50 +00:00
Andre601 35782809ac Merge pull request 'Update dependency org.apache.httpcomponents.client5:httpclient5 to v5.4.4' (#441) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/441
2025-04-25 12:08:42 +00:00
Andre601's Bot 3e3c4903e4 Update dependency org.apache.httpcomponents.client5:httpclient5 to v5.4.4 2025-04-25 12:02:49 +00:00
Andre601 79b45fb29d Merge pull request 'Update dependency com.google.code.gson:gson to v2.13.1' (#440) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/440
2025-04-24 14:20:27 +00:00
Andre601's Bot 46f40294e2 Update dependency com.google.code.gson:gson to v2.13.1 2025-04-24 14:08:23 +00:00
Andre601 ca5ab5d221 Merge pull request 'Update dependency mkdocs-material to v9.6.12' (#439) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/439
2025-04-17 12:04:54 +00:00
Andre601's Bot b2e5d7ae28 Update dependency mkdocs-material to v9.6.12 2025-04-17 12:02:51 +00:00
Andre601 649e9f07ef Merge pull request 'Update all non-major dependencies' (#438) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/438
2025-04-15 12:04:27 +00:00
Andre601's Bot eaa86aa93b Update all non-major dependencies 2025-04-15 12:02:50 +00:00
Andre601 8a90619810 Merge pull request 'Update dependency com.viaversion:viaversion-api to v5.3.2' (#437) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/437
2025-04-14 12:38:48 +00:00
Andre601's Bot 351db260ce Update dependency com.viaversion:viaversion-api to v5.3.2 2025-04-14 12:21:57 +00:00
Andre601 bcc9d88e8c Update appearance of playercount options in docs 2025-04-12 23:41:08 +00:00
Andre601 9c849157d3 Merge pull request 'Update all non-major dependencies' (#436) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/436
2025-04-12 12:12:05 +00:00
Andre601's Bot f171c951e7 Update all non-major dependencies 2025-04-12 12:02:23 +00:00
Andre601 cae808e3fc Add example for priority 2025-04-10 22:06:15 +02:00
Andre601 ae8e6304f9 Merge pull request 'Update dependency com.google.guava:guava to v33.4.7-jre' (#435) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/435
2025-04-09 12:26:44 +00:00
Andre601's Bot c975ba79b3 Update dependency com.google.guava:guava to v33.4.7-jre 2025-04-09 12:03:17 +00:00
Andre601 83fb13e1f2 Update wiki links 2025-04-07 23:49:05 +00:00
Andre601 586351e277 Update announcement banner 2025-04-08 01:46:40 +02:00
Andre601 098b6be0f4 Refactor profiles page 2025-04-08 01:40:36 +02:00
Andre601 1c881d2832 Update API Docs 2025-04-07 23:40:42 +02:00
Andre601 e6f3d122c7 Merge pull request 'Migrate renovate config' (#434) from renovate/migrate-config into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/434
2025-04-06 01:54:03 +00:00
Andre601's Bot 759474b344 Migrate config renovate.json 2025-04-06 01:52:21 +00:00
Andre601 6aef4c2de9 Enable config migration 2025-04-06 01:51:29 +00:00
Andre601 64d7c81c58 Merge pull request 'Update all non-major dependencies to v4.20.0' (#433) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/433
2025-04-06 01:30:06 +00:00
Andre601's Bot a070708323 Update all non-major dependencies to v4.20.0 2025-04-06 01:29:09 +00:00
Andre601 e9ba86508c Merge pull request 'Update dependency space.arim.libertybans:bans-api to v1.1.0' (#432) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/432
2025-04-05 12:36:42 +00:00
Andre601's Bot c1e3638534 Update dependency space.arim.libertybans:bans-api to v1.1.0 2025-04-05 12:02:18 +00:00
Andre601 f5260f3be1 Merge pull request 'Update dependency mkdocs-material to v9.6.11' (#431) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/431
2025-04-01 12:04:58 +00:00
Andre601's Bot 51372aa8f4 Update dependency mkdocs-material to v9.6.11 2025-04-01 12:02:14 +00:00
Andre601 025c9f4423 Merge pull request 'Update dependency com.viaversion:viaversion-api to v5.3.1' (#430) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/430
2025-03-31 12:25:08 +00:00
Andre601's Bot 99c34dd343 Update dependency com.viaversion:viaversion-api to v5.3.1 2025-03-31 12:02:53 +00:00
Andre601 cd91b799d6 Merge pull request 'Update dependency mkdocs-material to v9.6.10' (#429) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/429
2025-03-30 13:04:23 +00:00
Andre601's Bot 5b7eb014a3 Update dependency mkdocs-material to v9.6.10 2025-03-30 12:03:16 +00:00
Andre601 ad32952b97 Merge pull request 'Update dependency com.viaversion:viaversion-api to v5.3.0' (#428) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/428
2025-03-29 12:03:56 +00:00
Andre601's Bot 4a1e552d5d Update dependency com.viaversion:viaversion-api to v5.3.0 2025-03-29 12:02:13 +00:00
Andre601 ea1394dce9 Merge pull request 'Update all non-major dependencies' (#427) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/427
2025-03-28 12:10:16 +00:00
Andre601's Bot 317fa631e9 Update all non-major dependencies 2025-03-28 12:02:24 +00:00
Andre601 5dd26f6ab8 Merge pull request 'Update dependency org.apache.httpcomponents.client5:httpclient5 to v5.4.3' (#426) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/426
2025-03-27 12:05:14 +00:00
Andre601's Bot ba82df45ef Update dependency org.apache.httpcomponents.client5:httpclient5 to v5.4.3 2025-03-27 12:03:35 +00:00
Andre601 d76339188d Merge pull request 'Update all non-major dependencies' (#425) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/425
2025-03-26 12:21:12 +00:00
Andre601's Bot a9fb8f3ed6 Update all non-major dependencies 2025-03-26 12:02:54 +00:00
Andre601 18291a6865 Merge pull request 'Update dependency net.william278:papiproxybridge to v1.8' (#424) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/424
2025-03-23 13:12:05 +00:00
Andre601's Bot 83a1808ebc Update dependency net.william278:papiproxybridge to v1.8 2025-03-23 12:02:03 +00:00
Andre601 197967dea9 Use jspecify for (Non)Null annotations 2025-03-20 13:51:05 +01:00
Andre601 b25b65faba Merge pull request 'Update dependency com.google.guava:guava to v33.4.5-jre' (#423) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/423
2025-03-20 12:27:21 +00:00
Andre601's Bot fafcd3ae18 Update dependency com.google.guava:guava to v33.4.5-jre 2025-03-20 12:21:06 +00:00
Andre601 daae88074f Merge pull request 'Update dependency ch.qos.logback:logback-classic to v1.5.18' (#422) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/422
2025-03-19 12:02:45 +00:00
Andre601's Bot fbe019214d Update dependency ch.qos.logback:logback-classic to v1.5.18 2025-03-19 12:01:59 +00:00
Andre601 3e380342a7 Merge pull request 'Update dependency mkdocs-material to v9.6.9' (#421) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/421
2025-03-17 12:42:58 +00:00
Andre601's Bot fd2dbd2edb Update dependency mkdocs-material to v9.6.9 2025-03-17 12:02:33 +00:00
Andre601 e969208ae9 Merge pull request 'Update dependency org.junit.jupiter:junit-jupiter-engine to v5.12.1' (#420) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/420
2025-03-16 12:20:14 +00:00
Andre601's Bot 77aa404e06 Update dependency org.junit.jupiter:junit-jupiter-engine to v5.12.1 2025-03-16 12:10:40 +00:00
Andre601 a6c1840858 Merge pull request 'Update dependency mkdocs-material to v9.6.8' (#419) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/419
2025-03-13 12:35:37 +00:00
Andre601's Bot 762bd1d0fd Update dependency mkdocs-material to v9.6.8 2025-03-13 12:02:56 +00:00
Andre601 02ce580433 Merge pull request 'Update dependency com.github.LeonMangler:PremiumVanishAPI to v2.9.18-2' (#418) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/418
2025-03-09 12:19:41 +00:00
Andre601's Bot c7b7feb476 Update dependency com.github.LeonMangler:PremiumVanishAPI to v2.9.18-2 2025-03-09 12:06:02 +00:00
Andre601 0e145def83 Merge pull request 'Update dependency mkdocs-material to v9.6.7' (#417) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/417
2025-03-03 12:14:54 +00:00
Andre601's Bot 03e32feb1d Update dependency mkdocs-material to v9.6.7 2025-03-03 12:04:14 +00:00
Andre601 dbab282901 Merge pull request 'Update dependency mkdocs-material to v9.6.6' (#416) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/416
2025-03-01 12:10:43 +00:00
Andre601's Bot bd6b599267 Update dependency mkdocs-material to v9.6.6 2025-03-01 12:02:02 +00:00
Andre601 1a63430c1e Merge pull request 'Update all non-major dependencies' (#415) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/415
2025-02-26 12:14:58 +00:00
Andre601's Bot 661f9e6ad4 Update all non-major dependencies 2025-02-26 12:03:25 +00:00
Andre601 6bd8d40ff4 Remove temporary SVG Icon 2025-02-24 03:40:32 +01:00
Andre601 16b74433ff Merge pull request 'Update dependency org.junit.jupiter:junit-jupiter-engine to v5.12.0' (#414) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/414
2025-02-22 12:17:18 +00:00
Andre601's Bot 1aaafe474f Update dependency org.junit.jupiter:junit-jupiter-engine to v5.12.0 2025-02-22 12:03:51 +00:00
Andre601 26a7697b0f Merge pull request 'Update dependency org.apache.maven.plugins:maven-compiler-plugin to v3.14.0' (#413) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/413
2025-02-21 12:05:52 +00:00
Andre601's Bot ec6b653988 Update dependency org.apache.maven.plugins:maven-compiler-plugin to v3.14.0 2025-02-21 12:04:18 +00:00
Andre601 854eb67c54 Further API Doc updates 2025-02-20 14:56:29 +01:00
Andre601 00511f6e97 Update API doc page 2025-02-20 14:42:00 +01:00
Andre601 16eebb23a5 Update docs 2025-02-20 14:38:04 +01:00
Andre601 faae641c32 Merge pull request 'Update dependency mkdocs-material to v9.6.5' (#412) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/412
2025-02-20 12:57:14 +00:00
Andre601's Bot 4fa542a242 Update dependency mkdocs-material to v9.6.5 2025-02-20 12:02:25 +00:00
Andre601 2dc1f7ccc1 Update icon used for velocity + other stuff 2025-02-19 00:58:20 +01:00
Andre601 dd7b1a7392 Improve Expressions page 2025-02-17 20:21:20 +01:00
Andre601 a90d241cd2 Merge pull request 'Update all non-major dependencies to v4.19.0' (#411) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/411
2025-02-17 12:06:41 +00:00
Andre601's Bot 452ac00ee7 Update all non-major dependencies to v4.19.0 2025-02-17 12:03:06 +00:00
Andre601 f139270f0e Use inlinehilite 2025-02-16 21:50:38 +01:00
Andre601 b212b2af0f Add MiniMessage Pygments lexer to requirements.txt 2025-02-16 21:23:35 +01:00
Andre601 0d506379cf Add MiniMessage Pygment lexer 2025-02-16 21:22:21 +01:00
Andre601 995793e66d Update api-version and add safety-check to PaperCore 2025-02-16 21:20:54 +01:00
Andre601 f9ebc60d64 Add new SayanVanish placeholders 2025-02-13 19:18:39 +00:00
Andre601 1d531a32e3 Merge pull request 'Update all non-major dependencies to v1.6.5' (#410) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/410
2025-02-13 19:10:12 +00:00
Andre601's Bot 36340d9b9d Update all non-major dependencies to v1.6.5 2025-02-13 19:03:39 +00:00
Andre601 d023e22934 Merge pull request 'Update dependency mkdocs-material to v9.6.4' (#409) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/409
2025-02-12 13:52:19 +00:00
Andre601's Bot 260a0b4835 Update dependency mkdocs-material to v9.6.4 2025-02-12 12:02:41 +00:00
Andre601 3f18aaaf5b renovate.json aktualisiert 2025-02-11 19:56:22 +00:00
Andre601 3f0e7897d4 No longer use allorigins.win
Codeberg apparently no longer causes CORS issues for JS fetched requests.
2025-02-10 12:11:23 +00:00
Andre601 4bd35c810b Add subcommand permissions for profiles commands. 2025-02-09 02:58:52 +01:00
Andre601 b081be8f53 Improve Command docs 2025-02-09 02:40:58 +01:00
Andre601 eb474d061b Merge pull request 'Update dependency mkdocs-material to v9.6.3' (#407) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/407
2025-02-07 12:42:35 +00:00
Andre601 edbc893c65 Ignore 2.0.0-SNAPSHOT for cloud 2025-02-07 12:36:37 +00:00
Andre601's Bot d03dce9b29 Update all non-major dependencies 2025-02-07 12:02:26 +00:00
Andre601 de7d011216 Fix cloud-paper not being compiled into project 2025-02-06 13:45:21 +01:00
Andre601 4a2cb2078d Compile the bungee cloud dependency and make enum public. 2025-02-05 21:28:56 +01:00
Andre601 05381bd538 Rename CommandHandler to CommandManagerInterface 2025-02-04 23:17:05 +01:00
Andre601 51e561f2ba Use records instead of classes for Player implementations 2025-02-04 23:11:30 +01:00
Andre601 71a3bddc62 Remove no longer existing overrides and update docs 2025-02-04 22:16:09 +01:00
Andre601 3eeffd5520 Merge pull request 'Use Cloud for Command handling' (#400) from feature/cloud-command-system into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/400
2025-02-04 20:40:40 +00:00
Andre601 3590e4fa12 Merge pull request 'Update dependency mkdocs-material to v9.6.2' (#405) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/405
2025-02-04 12:54:04 +00:00
Andre601's Bot e1e7907d0f Update dependency mkdocs-material to v9.6.2 2025-02-04 12:02:22 +00:00
Andre601 1e8f71092c Update ExpressionParser 2025-02-04 02:33:47 +01:00
Andre601 1c12180fe3 Merge branch 'master' into feature/cloud-command-system 2025-02-03 17:54:42 +01:00
Andre601 7a41e27788 Merge pull request 'Use MkDocs macros plugin for API reference generation' (#398) from feature/docs/better-autogen-apidocs into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/398
2025-02-03 16:48:20 +00:00
Andre601 ddfc8763c4 Bump Sayanvanish to 1.6.4 and add it as addon to wiki 2025-02-03 14:28:38 +01:00
Andre601 086905ad53 Upate dependencies and saynvanish Repo URL 2025-02-03 14:09:49 +01:00
Andre601 4526e91e0f Add SayanVanish as third-party addon to the docs 2025-02-03 01:19:06 +01:00
Andre601 cfb74b277e Revert updating SayanVanishAPI to 1.6.3 to fix it being fully broken... 2025-02-02 14:40:43 +01:00
Andre601 d7c3a7d756 Merge pull request 'Update all non-major dependencies' (#403) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/403
2025-02-02 12:46:28 +00:00
Andre601's Bot 36c6af0a43 Update all non-major dependencies 2025-02-02 12:03:48 +00:00
Andre601 f9df11c9f8 Add Build ID on Jenkins Builds. 2025-02-01 23:09:38 +01:00
Andre601 8f42b63733 Merge pull request 'Update all non-major dependencies' (#402) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/402
2025-01-31 12:06:35 +00:00
Andre601's Bot 10604f4064 Update all non-major dependencies 2025-01-31 12:03:20 +00:00
Andre601 7a574513af Merge pull request 'Update dependency com.google.code.gson:gson to v2.12.0' (#401) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/401
2025-01-30 12:10:52 +00:00
Andre601's Bot deeea1578d Update dependency com.google.code.gson:gson to v2.12.0 2025-01-30 12:02:28 +00:00
Andre601 ac379b1668 Refactor some stuff 2025-01-30 02:12:04 +01:00
Andre601 c636764f4b Update default config in getting-started.md 2025-01-28 16:13:33 +00:00
Andre601 a823aebafd Exclude kyori in minecraft-extras dependency 2025-01-27 22:37:47 +01:00
Andre601 dd4c3c0eb3 Improve shading of dependencies 2025-01-27 22:32:37 +01:00
Andre601 23afe54e29 No need to use sublist (Is truncated in API) 2025-01-26 02:05:02 +01:00
Andre601 fab3731cf4 Update value change hover 2025-01-26 01:57:01 +01:00
Andre601 49a782d560 Fix broken inherits list for docs 2025-01-25 15:35:00 +00:00
Andre601 cdac325e71 Merge branch 'master' into feature/docs/better-autogen-apidocs 2025-01-25 15:24:16 +00:00
Andre601 88f4278f3f Implement different value types per option 2025-01-25 15:14:06 +01:00
Andre601 de4617e450 Display old and new value on setting 2025-01-24 23:52:41 +01:00
Andre601 4c4573b1e0 Add class to create Options list + /asl profiles set command 2025-01-23 23:39:11 +01:00
Andre601 f74f3ef766 Small restructure 2025-01-22 13:38:51 +01:00
Andre601 806a920d32 Update commands.md 2025-01-22 13:34:38 +01:00
Andre601 c9db8cbc97 Improve info structure a bit 2025-01-22 13:25:06 +01:00
Andre601 3e3cd112ec Change PlayerOnly to CommandType and use enum values 2025-01-22 03:05:53 +01:00
Andre601 fcc7d7c6b6 A loooooooot of work...
Yes, this was absolute pain to make, thanks for asking.
2025-01-22 02:51:15 +01:00
Andre601 cb31fc6f5a Use markdown for entries 2025-01-20 22:11:49 +01:00
Andre601 b26a614f87 Fix issues with Annotationa + add /asl profiles info {profile} 2025-01-20 17:21:35 +01:00
Andre601 eb6f4180bf Implement BungeeCord and Paper Command Handler 2025-01-19 21:13:02 +01:00
Andre601 02d6b1958b Add command alias + remove requiredSender declaration 2025-01-19 19:36:18 +01:00
Andre601 bf3c979819 Use Annotations for commands 2025-01-19 16:49:05 +01:00
Andre601 b5af4b9c47 Use Cloud for Command handling 2025-01-18 23:12:10 +01:00
Andre601 6b9cff2945 Merge pull request 'Update dependency mkdocs-material to v9.5.50' (#399) from renovate/all-minor-patch into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/399
2025-01-18 12:16:39 +00:00
Andre601's Bot d17549ee68 Update dependency mkdocs-material to v9.5.50 2025-01-18 12:01:47 +00:00
Andre601 8b73eb3c8c Change PAPI Placeholder behaviour + new placeholders 2025-01-16 02:27:31 +01:00
Andre601 1ee9ae473d Remove api-doc.html 2025-01-13 23:40:04 +01:00
Andre601 37006063cf Add macros plugin to requirements.txt 2025-01-13 23:37:53 +01:00
Andre601 76d97de029 Update API reference pages 2025-01-13 23:36:27 +01:00
Andre601 cd8f6f4c22 Merge branch 'master' into feature/docs/better-autogen-apidocs 2025-01-13 22:32:13 +01:00
Andre601 ce14274e4c Remove PlaceholderAPI page link 2025-01-12 15:34:31 +01:00
Andre601 a5d777f4f1 Restructure Wiki 2025-01-12 15:32:09 +01:00
Andre601 f94e578694 Update formatting.md 2025-01-10 15:28:57 +01:00
Andre601 4ff55ddf28 First work on some templates 2025-01-08 02:13:35 +01:00
Andre601 7c441e6ab5 Update SECURITY.md 2025-01-07 13:36:47 +00:00
Andre601 17d0802e41 Add MkDocs Macros Plugin 2024-12-28 16:14:10 +01:00
Andre601 db06de243f Add bluesky Post step to publish_assets.yml 2024-12-17 16:27:40 +00:00
200 changed files with 5964 additions and 4983 deletions

No files matched your search

+12 -18
View File
@@ -10,34 +10,28 @@ on:
- ".forgejo/workflows/deploy_wiki.yml"
- "docs/**"
- "domains"
- "mkdocs.yml"
- "properdocs.yml"
- "requirements.txt"
jobs:
deployWiki:
runs-on: codeberg-tiny
runs-on: codeberg-small
steps:
- name: "Checkout Source Repository"
uses: https://code.forgejo.org/actions/checkout@v4
- name: "Checkout Repository"
uses: https://code.forgejo.org/actions/checkout@v7
with:
token: "${{ secrets.BOT_TOKEN }}"
- name: "Checkout Target Repository"
run: "git clone -b pages https://codeberg.org/Andre601/asl-docs site"
- name: "Setup Python 3.x"
uses: https://code.forgejo.org/actions/setup-python@v5
uses: https://code.forgejo.org/actions/setup-python@v6
with:
python-version: 3.x
- name: "Install Dependencies"
run: "pip install -r requirements.txt"
- name: "Build Website"
run: "mkdocs build"
- name: "Commit and Push"
run: |
git config --global user.email "andre_601-bot@noreply.codeberg.org"
git config --global user.name "ASL Wiki Builder"
mv domains site/.domains
cd site
git remote set-url origin https://${{ secrets.BOT_TOKEN }}@codeberg.org/Andre601/asl-docs
git add --all
git commit -m "Update Wiki ($( env TZ=Europe/Zurich date +"%d.%m.%Y %Z" ))"
git push
run: "properdocs build"
- name: "Deploy Website"
uses: https://code.forgejo.org/actions/git-pages@v2
with:
site: https://asl.andre601.ch
token: ${{ secrets.FORGEJO_TOKEN }}
source: site/
+10 -10
View File
@@ -9,7 +9,7 @@ on:
paths:
- ".forgejo/workflows/pr_preview.yml"
- "docs/**"
- "mkdocs.yml"
- "properdocs.yml"
- "preview_domains"
- "requirements.txt"
workflow_dispatch:
@@ -20,7 +20,7 @@ env:
jobs:
createPreview:
runs-on: codeberg-tiny
runs-on: codeberg-small
steps:
#
# Finding and updating comment in Pull request.
@@ -28,13 +28,13 @@ jobs:
#
- name: "Find Comment"
id: find-comment
uses: https://github.com/peter-evans/find-comment@v3
uses: https://github.com/peter-evans/find-comment@v4
with:
issue-number: "${{ github.event.pull_request.number }}"
comment-author: "Andre_601-Bot"
- name: "Create or update Comment"
id: comment
uses: https://github.com/peter-evans/create-or-update-comment@v4
uses: https://github.com/peter-evans/create-or-update-comment@v5
with:
body: |-
## Pull request Preview
@@ -49,7 +49,7 @@ jobs:
# Build website and push.
#
- name: "Checkout Source Repository"
uses: https://code.forgejo.org/actions/checkout@v4
uses: https://code.forgejo.org/actions/checkout@v7
with:
token: ${{ secrets.BOT_TOKEN }}
fetch-depth: 0
@@ -60,13 +60,13 @@ jobs:
cd site
git switch ${{ env.BRANCH_NAME }} 2>/dev/null || git switch -c ${{ env.BRANCH_NAME }}
- name: "Setup Python 3.x"
uses: https://code.forgejo.org/actions/setup-python@v5
uses: https://code.forgejo.org/actions/setup-python@v6
with:
python-version: 3.x
- name: "Install Dependencies"
run: "pip install -r requirements.txt"
- name: "Build Website"
run: "mkdocs build"
run: "properdocs build"
- name: "Commit and Push"
run: |
git config --global user.email "andre_601-bot@noreply.codeberg.org"
@@ -81,7 +81,7 @@ jobs:
# Update comment on success or failure.
#
- name: "Comment Success"
uses: https://github.com/peter-evans/create-or-update-comment@v4
uses: https://github.com/peter-evans/create-or-update-comment@v5
with:
body: |-
## Pull request Preview
@@ -99,7 +99,7 @@ jobs:
edit-mode: replace
- name: "Comment Failure"
if: ${{ failure() }}
uses: https://github.com/peter-evans/create-or-update-comment@v4
uses: https://github.com/peter-evans/create-or-update-comment@v5
with:
body: |-
## Pull request Preview
@@ -109,7 +109,7 @@ jobs:
| Name | Link |
|---------|-------------------------------------------|
| Commit | ${{ github.event.pull_request.head.sha }} |
| Logs | ${{ RUN_URL }} |
| Logs | ${{ env.RUN_URL }} |
comment-id: "${{ steps.comment.outputs.comment-id }}"
issue-number: "${{ github.event.pull_request.number }}"
token: "${{ secrets.BOT_TOKEN }}"
-64
View File
@@ -1,64 +0,0 @@
when:
- event: pull_request
path:
include: [".woodpecker/pr_preview.yml", "docs/**", "mkdocs.yml", "requirements.txt"]
variables:
setup_git: &setup_git
- apk add git
- git config --global user.email "andre_601-bot@noreply.codeberg.org"
- git config --global user.name "PR Docs Preview Builder"
steps:
- name: "Clone Preview Repository"
image: alpine:3.20.3
commands:
- chmod -R a+w .
- <<: *setup_git
- git clone https://codeberg.org/Andre601/preview.git site
- chmod -R a+w site
- cd site
- git switch ${CI_REPO_NAME,,}/${CI_COMMIT_PULL_REQUEST} 2>/dev/null || git switch -c ${CI_REPO_NAME,,}/${CI_COMMIT_PULL_REQUEST}
- name: "Build Docs"
image: woodpeckerci/plugin-mkdocs:latest
settings:
pip_install_file: requirements.txt
- name: "Commit and Push"
image: alpine:3.20.3
secrets:
- bot_token
commands:
- <<: *setup_git
- mv preview-domains site/.domains
- cd site
- git remote add upstream https://$BOT_TOKEN@codeberg.org/Andre601/preview.git
- git add --all
- git commit -m "Update Preview for ${CI_REPO_NAME}/${CI_COMMIT_PULL_REQUEST} ($( env TZ=Europe/Zurich date +"%d.%m.%Y %Z" )) [SKIP CI]"
- git push --set-upstream upstream ${CI_REPO_NAME,,}/${CI_COMMIT_PULL_REQUEST} -f
- name: "Comment on Success"
image: mcs94/gitea-comment
when:
- status: ["success"]
settings:
gitea_address: https://codeberg.org
gitea_token:
from_secret: BOT_TOKEN
comment: |-
Pull request Preview Ready!
| Name | Value |
|--------|--------------------------------------------------------------------------|
| Commit | ${CI_COMMIT_SHA} |
| Link | https://preview.andre601.ch/@${CI_REPO_NAME,,}~${CI_COMMIT_PULL_REQUEST} |
- name: "Comment on Failure"
image: mcs94/gitea-comment
when:
- status: ["failure"]
settings:
gitea_address: https://codeberg.org
gitea_token:
from_secret: BOT_TOKEN
comment: |-
Pull request Preview **failed**!
Please check the CI logs for details: ${CI_PIPELINE_URL}
+19 -4
View File
@@ -4,7 +4,7 @@ when:
steps:
- name: "Build Jar and Upload"
image: maven:3.9.9-eclipse-temurin-21
image: maven:3.9.16-eclipse-temurin-25
environment:
CODEBERG_API_TOKEN:
from_secret: cb_token
@@ -19,16 +19,16 @@ steps:
- cd version-uploader/target
- mv VersionUploader.jar ../../
- cd ../../
- java -jar VersionUploader.jar --all
- java -jar VersionUploader.jar upload all
- name: "Upload Jars to Codeberg"
image: woodpeckerci/plugin-gitea-release
settings:
base_url: https://codeberg.org
files:
- "bungeecord/target/AdvancedServerList-*.jar"
- "paper/target/AdvancedServerList-*.jar"
- "velocity/target/AdvancedServerList-*.jar"
- "addons/banplugins/target/AdvancedServerList-*.jar"
- "addons/luckperms/target/AdvancedServerList-*.jar"
api_key:
from_secret: cb_token
- name: "Create Post on Mastodon"
@@ -45,4 +45,19 @@ steps:
Modrinth - https://modrinth.com/plugin/advancedserverlist
Hangar - https://hangar.papermc.io/Andre_601/AdvancedServerList
Codeberg - https://codeberg.org/Andre601/AdvancedServerList
visibility: public
visibility: public
- name: "Create Post on Bluesky"
image: woodpeckerci/plugin-bluesky-post
when:
- status: ["success"]
settings:
identifier: andre601.ch
app_password:
from_secret: bluesky_app_password
languages: english
text: |-
#AdvancedServerList was updated to ${CI_COMMIT_TAG} and can be downloaded from #Modrinth, #Hangar and #Codeberg!
Modrinth - https://modrinth.com/plugin/advancedserverlist
Hangar - https://hangar.papermc.io/Andre_601/AdvancedServerList
Codeberg - https://codeberg.org/Andre601/AdvancedServerList
-36
View File
@@ -1,36 +0,0 @@
when:
- event: push
branch: ${CI_REPO_DEFAULT_BRANCH}
path:
include: [".woodpecker/publish_docs.yml", "docs/**", "mkdocs.yml", "requirements.txt"]
variables:
setup_git: &setup_git
- apk add git
- git config --global user.email "andre_601-bot@noreply.codeberg.org"
- git config --global user.name "ASL Docs Builder"
steps:
- name: "Clone target Repository"
image: alpine:3.20.3
commands:
- chmod -R a+w .
- <<: *setup_git
- git clone -b pages https://codeberg.org/Andre601/asl-docs.git site
- chmod -R a+w site
- name: "Build Docs"
image: woodpeckerci/plugin-mkdocs:latest
settings:
pip_install_file: requirements.txt
- name: "Commit and Push"
image: alpine:3.20.3
secrets:
- bot_token
commands:
- <<: *setup_git
- mv domains site/.domains
- cd site
- git remote set-url origin https://$BOT_TOKEN@codeberg.org/Andre601/asl-docs.git
- git add --all
- git commit -m "Update AdvancedServerList Docs ($( env TZ=Europe/Zurich date +"%d.%m.%Y %Z" )) [SKIP CI]"
- git push
+8 -2
View File
@@ -40,7 +40,7 @@ A Server List Profile is a single YAML file located in the plugin's `profiles` f
Each file has a priority and optional condition. The priority is used to determine what file AdvancedServerList should use first - starting from highest number to lowest - with conditions being used to set when the profile should be used.
Once a profile has been found with a condition returning true - which is the default should there be no condition - will AdvancedServerList use said profile.
> *Read more about [Priority](https://asl.andre601.ch/profiles/#priority) and [Conditions](https://asl.andre601.ch/profiles/#condition)*
> *Read more about [Priority](https://asl.andre601.ch/profiles/options/priority) and [Conditions](https://asl.andre601.ch/profiles/options/condition)*
### Placeholders
@@ -63,7 +63,7 @@ AdvancedServerList soft-depends on the following plugins, meaning their inclusio
| Plugin | Supported Platforms |
|-------------------------------------------------------------------------|----------------------------------------|
| [PlaceholderAPI](https://hangar.papermc.io/HelpChat/PlaceholderAPI) | Paper |
| [PlaceholderAPI](https://modrinth.com/plugin/PlaceholderAPI) | Paper |
| [ViaVersion](https://hangar.papermc.io/ViaVersion/ViaVersion) | Paper |
| [PAPIProxyBridge](https://hangar.papermc.io/William278/PAPIProxyBridge) | BungeeCord, Waterfall, Velocity |
| [Maintenance](https://hangar.papermc.io/kennytv/Maintenance) | Paper, BungeeCord, Waterfall, Velocity |
@@ -92,6 +92,12 @@ English Video by KasaiSora
Spanish Video by Ajneb97
[![video thumbnail](https://img.youtube.com/vi/rIbljm_4HVI/0.jpg)](https://youtube.com/watch?v=rIbljm_4HVI)
Indonesian Video by faajrikha
[![video thumbnail](https://img.youtube.com/vi/-p938rOmqy4/0.jpg)](https://www.youtube.com/watch?v=-p938rOmqy4)
German Video by Finni Gamer
[![video thumbnail](https://img.youtube.com/vi/1Kh3cPMDd0c/0.jpg)](https://www.youtube.com/watch?v=1Kh3cPMDd0c)
</p>
</details>
+7 -7
View File
@@ -8,17 +8,19 @@ To ensure proper safety and support are only certain versions and Platforms of A
|---------------|-----------|
| v5 | Yes |
| v4 | Yes* |
| v3 | Yes** |
| v3 | No** |
| v2 | No |
| v1 | No |
\*Only receives support but no updates or bug fixes.
\*\*Support ends at the end of 2024.
\*\*Support ended on December 31st, 2024.
## Supported Server/Proxy Versions
| Server/Proxy Version* | Supported |
|-----------------------|-----------|
| 26.2 | Yes |
| 26.1.x | Yes |
| 1.21.x | Yes |
| 1.20.x | Yes |
| 1.19.x | No |
@@ -53,8 +55,6 @@ To ensure proper safety and support are only certain versions and Platforms of A
| Proxy | Supported |
|------------|-----------|
| BungeeCord | Yes** |
| Waterfall | Yes** |
| Velocity | Yes |
\*\*Platform version of this plugin is maintenance only, not receiving any bug fixes for major breaking changes anymore.
| BungeeCord | No |
| Waterfall | No |
| Velocity | Yes |
+7 -28
View File
@@ -40,8 +40,8 @@
<version>${plugin.version}</version>
<properties>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
@@ -62,30 +62,16 @@
<dependencies>
<!-- Proxy/Server -->
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.20-R0.2</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.20-R0.2</version>
<type>javadoc</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.21-R0.1-SNAPSHOT</version>
<version>[26.1.2.build,)</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.velocitypowered</groupId>
<artifactId>velocity-api</artifactId>
<version>3.3.0-SNAPSHOT</version>
<version>3.4.0</version>
<scope>provided</scope>
</dependency>
@@ -99,13 +85,13 @@
<dependency>
<groupId>space.arim.libertybans</groupId>
<artifactId>bans-api</artifactId>
<version>1.1.0-SNAPSHOT</version>
<version>1.1.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.gitlab.ruany</groupId>
<artifactId>LiteBansAPI</artifactId>
<version>0.5.0</version>
<version>0.6.1</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -117,19 +103,12 @@
</dependencies>
<build>
<finalName>AdvancedServerList-BanPlugins-Addon-${plugin.version}</finalName>
<finalName>AdvancedServerList-BanPlugins-Addon-${plugin.version}${project.build.build_id}</finalName>
<resources>
<resource>
<filtering>true</filtering>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
</plugin>
</plugins>
</build>
</project>
@@ -1,68 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2022-2024 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.banplugins.bungeecord;
import ch.andre601.advancedserverlist.api.AdvancedServerListAPI;
import ch.andre601.advancedserverlist.banplugins.BanPluginsList;
import net.md_5.bungee.api.plugin.Plugin;
public class BungeeCordBanPluginsAddon extends Plugin{
@Override
public void onEnable(){
if(getProxy().getPluginManager().getPlugin("AdvancedServerList") == null){
getLogger().warning("AdvancedServerList is not enabled. This plugin requires it to function!");
return;
}
int loadedPlaceholders = loadPlaceholderProviders();
if(loadedPlaceholders == 0){
getLogger().warning("No compatible Ban plugin was found to register placeholders for.");
}else{
getLogger().info("Loaded " + loadedPlaceholders + " Placeholder Set(s) for AdvancedServerList!");
}
}
private int loadPlaceholderProviders(){
int loaded = 0;
AdvancedServerListAPI api = AdvancedServerListAPI.get();
for(BanPluginsList entry : BanPluginsList.values()){
if(!entry.supportsBungeeCord())
continue;
if(getProxy().getPluginManager().getPlugin(entry.getName()) != null){
getLogger().info("Registering Placeholders for " + entry.getName() + "...");
api.addPlaceholderProvider(entry.getPlaceholderProvider());
getLogger().info("Placeholders successfully registered!");
loaded++;
}
}
return loaded;
}
}
@@ -1,12 +0,0 @@
name: 'ASLBanPluginsAddon'
author: 'Andre_601'
version: '${plugin.version}'
description: 'AdvancedBan Add-on for AdvancedServerList to add placeholders to use.'
softDepends:
- AdvancedServerList
- AdvancedBan
- LibertyBans
- LiteBans
main: 'ch.andre601.advancedserverlist.banplugins.bungeecord.BungeeCordBanPluginsAddon'
+112
View File
@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ MIT License
~
~ Copyright (c) 2022-2024 Andre_601
~
~ Permission is hereby granted, free of charge, to any person obtaining a copy
~ of this software and associated documentation files (the "Software"), to deal
~ in the Software without restriction, including without limitation the rights
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
~ copies of the Software, and to permit persons to whom the Software is
~ furnished to do so, subject to the following conditions:
~
~ The above copyright notice and this permission notice shall be included in all
~ copies or substantial portions of the Software.
~
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
~ SOFTWARE.
~
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>parent</artifactId>
<groupId>ch.andre601.advancedserverlist</groupId>
<version>parent</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>luckperms</artifactId>
<packaging>jar</packaging>
<version>${plugin.version}</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>sonatype</id>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</repository>
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<!-- Proxy/Server -->
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.21-R0.4</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.21-R0.4</version>
<type>javadoc</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>[26.1.2.build,)</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.velocitypowered</groupId>
<artifactId>velocity-api</artifactId>
<version>3.4.0</version>
<scope>provided</scope>
</dependency>
<!-- Plugin APIs -->
<dependency>
<groupId>ch.andre601.asl-api</groupId>
<artifactId>api</artifactId>
<version>${api.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.luckperms</groupId>
<artifactId>api</artifactId>
<version>5.5</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>AdvancedServerList-LuckPerms-Addon-${plugin.version}${project.build.build_id}</finalName>
<resources>
<resource>
<filtering>true</filtering>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
</project>
@@ -0,0 +1,44 @@
package ch.andre601.advancedserverlist.luckperms.paper;
import ch.andre601.advancedserverlist.api.AdvancedServerListAPI;
import ch.andre601.advancedserverlist.api.exceptions.InvalidPlaceholderProviderException;
import ch.andre601.advancedserverlist.luckperms.placeholders.LuckPermsPlaceholder;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.LuckPermsProvider;
import org.bukkit.plugin.java.JavaPlugin;
public class LuckPermsAddon extends JavaPlugin{
@Override
public void onEnable(){
if(!this.getServer().getPluginManager().isPluginEnabled("AdvancedServerList")){
this.getSLF4JLogger().warn("AdvancedServerList not enabled. This plugin is required!");
this.getServer().getPluginManager().disablePlugin(this);
return;
}
if(!this.getServer().getPluginManager().isPluginEnabled("LuckPerms")){
this.getSLF4JLogger().warn("LuckPerms not enabled. This plugin is required!");
this.getServer().getPluginManager().disablePlugin(this);
return;
}
LuckPerms luckPerms;
try{
luckPerms = LuckPermsProvider.get();
}catch(IllegalStateException ex){
this.getSLF4JLogger().error("Unable to obtain LuckPerms API instance! Disabling plugin...");
this.getServer().getPluginManager().disablePlugin(this);
return;
}
this.getSLF4JLogger().info("Registering LuckPerms Placeholder...");
try{
AdvancedServerListAPI.get().addPlaceholderProvider(new LuckPermsPlaceholder(luckPerms));
this.getSLF4JLogger().info("Successfully registered Placeholder!");
}catch(InvalidPlaceholderProviderException ex){
this.getSLF4JLogger().warn("Couldn't register Placeholder. Disabling Plugin...");
this.getServer().getPluginManager().disablePlugin(this);
}
}
}
@@ -0,0 +1,66 @@
package ch.andre601.advancedserverlist.luckperms.placeholders;
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.model.user.User;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import java.util.HashMap;
import java.util.UUID;
public class LuckPermsPlaceholder extends PlaceholderProvider{
private final LuckPerms luckPerms;
private final HashMap<UUID, CacheValue> cache = new HashMap<>();
public LuckPermsPlaceholder(LuckPerms luckPerms){
super("luckperms");
this.luckPerms = luckPerms;
}
@Override
public @Nullable String parsePlaceholder(@NonNull String placholder, @NonNull GenericPlayer player, @NonNull GenericServer server){
if(luckPerms == null)
return "ERR: LuckPerms not available.";
CacheValue cacheValue = cache.get(player.getUUID());
User user;
if(cacheValue == null || cacheValue.expired()){
// Can cause lag if on main thread.
// But the alternative would be a lot more annoying and error-prone.
user = luckPerms.getUserManager().loadUser(player.getUUID()).join();
cache.put(player.getUUID(), new CacheValue(System.currentTimeMillis(), user));
}else{
user = cacheValue.user();
}
if(user == null)
return "ERR: Player not found.";
String[] parts = placholder.split("\\s+");
if(parts.length == 1)
return "ERR: Missing argument for placeholder " + parts[0] + ".";
return switch(parts[0]){
case "hasGroup" -> String.valueOf(
user.getCachedData().getPermissionData().checkPermission("group." + parts[1])
);
case "hasPermission" -> String.valueOf(
user.getCachedData().getPermissionData().checkPermission(parts[1]).asBoolean()
);
case "primaryGroup" -> user.getPrimaryGroup();
default -> null;
};
}
private record CacheValue(long timestamp, User user){
public boolean expired(){
// Expired if current timestamp minus old one is more than 60k milliseconds (1 minute)
return (timestamp < 0L) || System.currentTimeMillis() - timestamp >= 60_000;
}
}
}
@@ -0,0 +1,53 @@
package ch.andre601.advancedserverlist.luckperms.velocity;
import ch.andre601.advancedserverlist.api.AdvancedServerListAPI;
import ch.andre601.advancedserverlist.api.exceptions.InvalidPlaceholderProviderException;
import ch.andre601.advancedserverlist.luckperms.placeholders.LuckPermsPlaceholder;
import com.google.inject.Inject;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.proxy.ProxyServer;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.LuckPermsProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LuckPermsAddon{
private final ProxyServer proxy;
private final Logger logger = LoggerFactory.getLogger("ASLLuckPermsAddon");
@Inject
public LuckPermsAddon(ProxyServer proxy){
this.proxy = proxy;
}
@Subscribe
public void init(ProxyInitializeEvent event){
if(!proxy.getPluginManager().isLoaded("advancedserverlist")){
logger.warn("AdvancedServerList is not enabled. This plugin is required.");
return;
}
if(!proxy.getPluginManager().isLoaded("luckperms")){
logger.warn("LuckPerms is not enabled. This plugin is required.");
return;
}
LuckPerms luckPerms;
try{
luckPerms = LuckPermsProvider.get();
}catch(IllegalStateException ex){
logger.warn("Unable to obtain LuckPerms API instance. The plugin won't work.");
return;
}
logger.info("Registering LuckPerms Placeholder...");
try{
AdvancedServerListAPI.get().addPlaceholderProvider(new LuckPermsPlaceholder(luckPerms));
logger.info("Successfully registered Placeholder!");
}catch(InvalidPlaceholderProviderException ex){
logger.warn("Unable to register LuckPerms Placeholder! The plugin won't work.");
}
}
}
@@ -0,0 +1,16 @@
name: 'ASLLuckPermsAddon'
author: 'Andre_601'
version: '${plugin.version}'
main: 'ch.andre601.advancedserverlist.luckperms.paper.LuckPermsAddon'
api-version: '1.19'
dependencies:
server:
AdvancedServerList:
required: false
load: BEFORE
LuckPerms:
required: false
load: BEFORE
@@ -0,0 +1,20 @@
{
"id": "aslluckpermsaddon",
"name": "ASLLuckPermsAddon",
"version": "${plugin.version}",
"description": "LuckPerms Add-on for AdvancedServerList to add placeholders to use.",
"authors": [
"Andre_601"
],
"dependencies": [
{
"id": "advancedserverlist",
"optional": true
},
{
"id": "luckperms",
"optional": true
}
],
"main": "ch.andre601.advancedserverlist.luckperms.velocity.LuckPermsAddon"
}
-206
View File
@@ -1,206 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ MIT License
~
~ Copyright (c) 2022-2023 Andre_601
~
~ Permission is hereby granted, free of charge, to any person obtaining a copy
~ of this software and associated documentation files (the "Software"), to deal
~ in the Software without restriction, including without limitation the rights
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
~ copies of the Software, and to permit persons to whom the Software is
~ furnished to do so, subject to the following conditions:
~
~ The above copyright notice and this permission notice shall be included in all
~ copies or substantial portions of the Software.
~
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
~ SOFTWARE.
~
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>parent</artifactId>
<groupId>ch.andre601.advancedserverlist</groupId>
<version>parent</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>bungeecord</artifactId>
<packaging>jar</packaging>
<version>${plugin.version}</version>
<description>${plugin.description}</description>
<properties>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
</properties>
<repositories>
<repository>
<id>sonatype</id>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</repository>
<repository>
<id>repo-sayandevelopment</id>
<url>https://repo.sayandev.org/snapshots</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>ch.andre601.advancedserverlist</groupId>
<artifactId>core</artifactId>
<version>${plugin.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ch.andre601.asl-api</groupId>
<artifactId>platform-bungeecord</artifactId>
<version>${api.version}</version>
<scope>compile</scope>
</dependency>
<!-- BungeeCord API -->
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.21-R0.1-SNAPSHOT</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.21-R0.1-SNAPSHOT</version>
<type>javadoc</type>
<scope>provided</scope>
</dependency>
<!-- Adventure Library -->
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-api</artifactId>
<version>${dependency.adventure-api}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-minimessage</artifactId>
<version>${dependency.adventure-text-minimessage}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-platform-bungeecord</artifactId>
<version>${dependency.adventure-platform-bungeecord}</version>
<scope>provided</scope>
</dependency>
<!-- Metrics -->
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bungeecord</artifactId>
<version>3.1.0</version>
<scope>compile</scope>
</dependency>
<!-- Configurate -->
<dependency>
<groupId>org.spongepowered</groupId>
<artifactId>configurate-yaml</artifactId>
<version>${dependency.configurate-yaml}</version>
<scope>provided</scope>
</dependency>
<!-- Plugins -->
<dependency>
<groupId>com.github.LeonMangler</groupId>
<artifactId>PremiumVanishAPI</artifactId>
<version>2.9.0-4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.sayandev</groupId>
<artifactId>sayanvanish-api</artifactId>
<version>1.6.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.sayandev</groupId>
<artifactId>sayanvanish-proxy-bungeecord</artifactId>
<version>1.6.1</version>
<scope>provided</scope>
</dependency>
<!-- Other -->
<dependency>
<groupId>com.alessiodp.libby</groupId>
<artifactId>libby-bungee</artifactId>
<version>2.0.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<finalName>AdvancedServerList-BungeeCord-${plugin.version}</finalName>
<resources>
<resource>
<filtering>true</filtering>
<directory>${project.basedir}/src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.bstats</shadedPattern>
</relocation>
<relocation>
<pattern>net.byteflux.libby</pattern>
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.libby</shadedPattern>
</relocation>
</relocations>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -1,251 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2022-2023 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.bungeecord;
import ch.andre601.advancedserverlist.bungeecord.commands.CmdAdvancedServerList;
import ch.andre601.advancedserverlist.bungeecord.listeners.JoinEvent;
import ch.andre601.advancedserverlist.bungeecord.listeners.PingEvent;
import ch.andre601.advancedserverlist.bungeecord.logging.BungeeLogger;
import ch.andre601.advancedserverlist.bungeecord.objects.placeholders.BungeePlayerPlaceholders;
import ch.andre601.advancedserverlist.bungeecord.objects.placeholders.BungeeProxyPlaceholders;
import ch.andre601.advancedserverlist.bungeecord.objects.placeholders.BungeeServerPlaceholders;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
import ch.andre601.advancedserverlist.core.profiles.handlers.FaviconHandler;
import com.alessiodp.libby.BungeeLibraryManager;
import com.alessiodp.libby.Library;
import de.myzelyam.api.vanish.BungeeVanishAPI;
import net.kyori.adventure.platform.bungeecord.BungeeAudiences;
import net.md_5.bungee.api.Favicon;
import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Plugin;
import org.bstats.bungeecord.Metrics;
import org.bstats.charts.SimplePie;
import org.sayandev.sayanvanish.bungeecord.api.SayanVanishBungeeAPI;
import java.awt.image.BufferedImage;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
public class BungeeCordCore extends Plugin implements PluginCore<Favicon>{
private final PluginLogger logger = new BungeeLogger(this);
private final Map<String, ServerPing> fetchedServers = new ConcurrentHashMap<>();
private boolean successfulLoad = false;
private AdvancedServerList<Favicon> core;
private FaviconHandler<Favicon> faviconHandler = null;
private BungeeAudiences audiences = null;
private BungeeLibraryManager libraryManager = null;
@Override
public void onLoad(){
// Using getLogger() here, because PluginLogger requires MiniMessage, which isn't loaded at this stage.
getLogger().info("Loading libraries. This may take a while...");
successfulLoad = loadLibs();
if(successfulLoad){
getLogger().info("Library loading completed successfully!");
}else{
getLogger().warning("Library loading completed with errors!");
}
}
@Override
public void onEnable(){
if(!successfulLoad){
logger.warn("There were issues during the plugins Loading-phase! Please see the console for possible reasons.");
logger.warn("Aborting further enabling of the plugin to avoid errors.");
return;
}
this.audiences = BungeeAudiences.create(this);
this.core = AdvancedServerList.init(this,
BungeePlayerPlaceholders.init(), BungeeServerPlaceholders.init(this), BungeeProxyPlaceholders.init(this));
}
@Override
public void onDisable(){
core.disable();
getProxy().getScheduler().cancel(this);
audiences.close();
}
@Override
public void loadCommands(){
getProxy().getPluginManager().registerCommand(this, new CmdAdvancedServerList(this));
getPluginLogger().success("Registered <white>/advancedserverlist</white>!");
}
@Override
public void loadEvents(){
new JoinEvent(this);
new PingEvent(this);
}
@Override
public void loadMetrics(){
new Metrics(this, 15585).addCustomChart(new SimplePie("profiles",
() -> String.valueOf(core.getFileHandler().getProfiles().size())
));
}
@Override
public void loadFaviconHandler(AdvancedServerList<Favicon> core){
faviconHandler = new FaviconHandler<>(core);
}
@Override
public void clearFaviconCache(){
if(faviconHandler == null)
return;
faviconHandler.cleanCache();
}
@Override
public void downloadLibrary(String groupId, String artifactId, String version){
if(libraryManager == null){
libraryManager = new BungeeLibraryManager(this);
libraryManager.addRepository("https://repo.papermc.io/repository/maven-public");
}
Library lib = Library.builder()
.groupId(groupId)
.artifactId(artifactId)
.version(version)
.resolveTransitiveDependencies(true)
.build();
libraryManager.loadLibrary(lib);
}
@Override
public void startScheduler(){
getProxy().getScheduler().schedule(this, this::fetchServers, 0, 10, TimeUnit.SECONDS);
}
@Override
public AdvancedServerList<Favicon> getCore(){
return core;
}
@Override
public Path getFolderPath(){
return getDataFolder().toPath();
}
@Override
public PluginLogger getPluginLogger(){
return logger;
}
@Override
public FaviconHandler<Favicon> getFaviconHandler(){
if(faviconHandler == null)
faviconHandler = new FaviconHandler<>(core);
return faviconHandler;
}
@Override
public String getPlatformInfo(){
return getProxy().getName() + " " + getProxy().getVersion();
}
@Override
public String getLoader(){
return "bungeecord";
}
@Override
public boolean isPluginEnabled(String plugin){
return getProxy().getPluginManager().getPlugin(plugin) != null;
}
@Override
public Favicon createFavicon(BufferedImage image) throws IllegalArgumentException{
return Favicon.create(image);
}
public BungeeAudiences getAudiences(){
return audiences;
}
public int getOnlinePlayers(ServerInfo server){
List<ProxiedPlayer> players = new ArrayList<>(server == null ? getProxy().getPlayers() : server.getPlayers());
// Exclude players when PremiumVanish is enabled and player is hidden.
if(getProxy().getPluginManager().getPlugin("PremiumVanish") != null){
players.removeIf(BungeeVanishAPI::isInvisible);
}else // Do the same if SayanVanish is enabled.
if(getProxy().getPluginManager().getPlugin("SayanVanish") != null){
players.removeIf(player -> SayanVanishBungeeAPI.getInstance().isVanished(player.getUniqueId()));
}
return players.size();
}
public Map<String, ServerPing> getFetchedServers(){
return fetchedServers;
}
private boolean loadLibs(){
try{
if(libraryManager == null){
libraryManager = new BungeeLibraryManager(this);
libraryManager.addRepository("https://repo.papermc.io/repository/maven-public");
}
libraryManager.configureFromJSON("dependencies.json");
return true;
}catch(Exception ex){
getLogger().log(Level.WARNING, "Encountered an Exception while trying to load dependencies.", ex);
return false;
}
}
private void fetchServers(){
fetchedServers.clear();
getProxy().getServers().forEach((name, server) -> server.ping((ping, throwable) -> {
if(throwable != null)
return;
fetchedServers.put(name, ping);
}));
}
}
@@ -1,52 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2022-2023 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.bungeecord.commands;
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import net.kyori.adventure.platform.bungeecord.BungeeAudiences;
import net.md_5.bungee.api.CommandSender;
public class BungeeCmdSender implements CmdSender{
private final CommandSender sender;
private final BungeeAudiences bungeeAudiences;
public BungeeCmdSender(CommandSender sender, BungeeAudiences bungeeAudiences){
this.sender = sender;
this.bungeeAudiences = bungeeAudiences;
}
@Override
public boolean hasPermission(String permission){
return sender.hasPermission(permission) || sender.hasPermission("advancedserverlist.admin");
}
@Override
public void sendMsg(String msg, Object... args){
bungeeAudiences.sender(sender).sendMessage(ComponentParser.text(String.format(msg, args)).toComponent());
}
}
@@ -1,46 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2022-2023 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.bungeecord.commands;
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
import net.kyori.adventure.platform.bungeecord.BungeeAudiences;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.plugin.Command;
public class CmdAdvancedServerList extends Command{
private final BungeeCordCore plugin;
public CmdAdvancedServerList(BungeeCordCore plugin){
super("advancedserverlist", "advancedserverlist.command.asl", "asl");
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args){
plugin.getCore().getCommandHandler().handle(new BungeeCmdSender(sender, plugin.getAudiences()), args);
}
}
@@ -1,219 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2022-2023 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.bungeecord.listeners;
import ch.andre601.advancedserverlist.api.events.GenericServerListEvent;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
import ch.andre601.advancedserverlist.bungeecord.objects.impl.BungeePlayerImpl;
import ch.andre601.advancedserverlist.bungeecord.objects.impl.BungeeProxyImpl;
import ch.andre601.advancedserverlist.core.compat.maintenance.MaintenanceUtil;
import ch.andre601.advancedserverlist.core.compat.papi.PAPIUtil;
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
import ch.andre601.advancedserverlist.core.interfaces.events.GenericEventWrapper;
import ch.andre601.advancedserverlist.core.objects.CachedPlayer;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import ch.andre601.advancedserverlist.core.profiles.replacer.StringReplacer;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import net.md_5.bungee.api.Favicon;
import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.ProxyPingEvent;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class BungeeEventWrapper implements GenericEventWrapper<Favicon, BungeePlayerImpl>{
private final BungeeCordCore plugin;
private final ProxyPingEvent event;
private final ServerPing.Protocol protocol;
private final ServerPing ping;
public BungeeEventWrapper(BungeeCordCore plugin, ProxyPingEvent event){
this.plugin = plugin;
this.event = event;
this.protocol = event.getResponse().getVersion();
this.ping = event.getResponse();
}
@Override
public GenericServerListEvent callEvent(ProfileEntry entry){
PreServerListSetEventImpl event = new PreServerListSetEventImpl(entry);
plugin.getProxy().getPluginManager().callEvent(event);
return event;
}
@Override
public void setMaxPlayers(int maxPlayers){
ping.getPlayers().setMax(maxPlayers);
}
@Override
public void setOnlinePlayers(int onlinePlayers){
ping.getPlayers().setOnline(onlinePlayers);
}
@Override
public void setMotd(Component component){
ping.setDescriptionComponent(new TextComponent(BungeeComponentSerializer.get().serialize(component)));
}
@Override
public void hidePlayers(){
ping.setPlayers(null);
}
@Override
public void setPlayerCount(String name){
protocol.setName(name);
protocol.setProtocol(-1);
}
@Override
public void setPlayers(List<String> lines, BungeePlayerImpl player, GenericServer server){
ServerPing.PlayerInfo[] players = new ServerPing.PlayerInfo[lines.size()];
for(int i = 0; i < players.length; i++){
String parsed = ComponentParser.text(lines.get(i))
.modifyText(text -> StringReplacer.replace(text, player, server))
.modifyText(text -> parsePAPIPlaceholders(text, player))
.toString();
ServerPing.PlayerInfo pi = new ServerPing.PlayerInfo(parsed, UUID.randomUUID());
players[i] = pi;
}
if(players.length > 0)
ping.getPlayers().setSample(players);
}
@Override
public void setPlayersHidden(){
ping.getPlayers().setSample(new ServerPing.PlayerInfo[0]);
}
@Override
public void setFavicon(Favicon favicon){
ping.setFavicon(favicon);
}
@Override
public void setDefaultFavicon(){
ping.setFavicon(ping.getFaviconObject());
}
@Override
public void updateEvent(){
this.ping.setVersion(this.protocol);
this.event.setResponse(this.ping);
}
@Override
public boolean isInvalidProtocol(){
return protocol == null;
}
@Override
public boolean isMaintenanceModeActive(){
if(plugin.getProxy().getPluginManager().getPlugin("Maintenance") == null)
return false;
return MaintenanceUtil.get().isMaintenanceEnabled();
}
@Override
public int getProtocolVersion(){
return event.getConnection().getVersion();
}
@Override
public int getOnlinePlayers(){
return plugin.getOnlinePlayers(null);
}
@Override
public int getMaxPlayers(){
return ping.getPlayers().getMax();
}
@Override
public String getPlayerIP(){
return ((InetSocketAddress)event.getConnection().getSocketAddress()).getHostString();
}
@Override
public String parsePAPIPlaceholders(String text, BungeePlayerImpl player){
if(plugin.getProxy().getPluginManager().getPlugin("PAPIProxyBridge") == null)
return text;
if(!PAPIUtil.get().isCompatible())
return text;
String server = PAPIUtil.get().getServer();
if(server == null || server.isEmpty())
return text;
ServerInfo serverInfo = plugin.getProxy().getServerInfo(server);
if(serverInfo == null || serverInfo.getPlayers().isEmpty())
return text;
ProxiedPlayer carrier = PAPIUtil.get().getPlayer(serverInfo.getPlayers());
if(carrier == null)
return text;
return PAPIUtil.get().parse(text, carrier.getUniqueId(), player.getUUID());
}
@Override
public String getVirtualHost(){
return this.resolveHost(event.getConnection().getVirtualHost());
}
@Override
public PluginCore<Favicon> getPlugin(){
return plugin;
}
@Override
public BungeePlayerImpl createPlayer(CachedPlayer player, int protocol){
return new BungeePlayerImpl(player, protocol);
}
@Override
public GenericServer createGenericServer(int playersOnline, int playersMax, String host){
Map<String, ServerInfo> servers = plugin.getProxy().getServers();
return new BungeeProxyImpl(servers, playersOnline, playersMax, host);
}
}
@@ -1,61 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2022-2023 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.bungeecord.listeners;
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
import ch.andre601.advancedserverlist.bungeecord.commands.BungeeCmdSender;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import java.net.InetSocketAddress;
public class JoinEvent implements Listener{
private final BungeeCordCore plugin;
public JoinEvent(BungeeCordCore plugin){
this.plugin = plugin;
plugin.getProxy().getPluginManager().registerListener(plugin, this);
}
@EventHandler
public void onJoin(PostLoginEvent event){
InetSocketAddress address = (InetSocketAddress)event.getPlayer().getPendingConnection().getSocketAddress();
ProxiedPlayer player = event.getPlayer();
plugin.getCore().getPlayerHandler().addPlayer(address.getHostString(), player.getName(), player.getUniqueId());
if(player.hasPermission("advancedserverlist.admin") || player.hasPermission("advancedserverlist.updatecheck")){
if(plugin.getAudiences() == null || plugin.getCore().getUpdateChecker() == null)
return;
BungeeCmdSender sender = new BungeeCmdSender(player, plugin.getAudiences());
plugin.getCore().getUpdateChecker().performCachedUpdateCheck(sender);
}
}
}
@@ -1,51 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2022-2023 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.bungeecord.listeners;
import ch.andre601.advancedserverlist.api.bungeecord.events.PostServerListSetEvent;
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
import ch.andre601.advancedserverlist.core.events.PingEventHandler;
import net.md_5.bungee.api.event.ProxyPingEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
public class PingEvent implements Listener{
private final BungeeCordCore plugin;
public PingEvent(BungeeCordCore plugin){
this.plugin = plugin;
plugin.getProxy().getPluginManager().registerListener(plugin, this);
}
@EventHandler(priority = 90) // Maintenance has Event Priority 80, so we need to be AFTER it.
public void onProxyPing(ProxyPingEvent event){
ProfileEntry entry = PingEventHandler.handleEvent(new BungeeEventWrapper(plugin, event));
plugin.getProxy().getPluginManager().callEvent(new PostServerListSetEvent(entry));
}
}
@@ -1,76 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2022-2023 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.bungeecord.logging;
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import java.util.logging.Level;
import java.util.logging.Logger;
public class BungeeLogger implements PluginLogger{
private final BungeeCordCore plugin;
private final Logger logger;
public BungeeLogger(BungeeCordCore plugin){
this.plugin = plugin;
this.logger = plugin.getLogger();
}
@Override
public void debug(Class<?> clazz, String msg, Object... args){
if(plugin.isDebugEnabled())
info("[DEBUG] [" + clazz.getSimpleName() + "] " + msg, args);
}
@Override
public void debugWarn(Class<?> clazz, String msg, Object... args){
if(plugin.isDebugEnabled())
warn("[DEBUG] [" + clazz.getSimpleName() + "] " + msg, args);
}
@Override
public void info(String msg, Object... args){
logger.info(ComponentParser.textFormatted(msg, args).toStringStriped());
}
@Override
public void warn(String msg, Object... args){
logger.warning(ComponentParser.textFormatted(msg, args).toStringStriped());
}
@Override
public void warn(String msg, Throwable throwable){
logger.log(Level.WARNING, ComponentParser.text(msg).toStringStriped(), throwable);
}
@Override
public void warn(String msg, Throwable throwable, Object... args){
logger.log(Level.WARNING, ComponentParser.textFormatted(msg, args).toStringStriped(), throwable);
}
}
@@ -1,59 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2022-2024 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.bungeecord.objects.impl;
import ch.andre601.advancedserverlist.api.bungeecord.objects.BungeePlayer;
import ch.andre601.advancedserverlist.core.objects.CachedPlayer;
import java.util.UUID;
public class BungeePlayerImpl implements BungeePlayer{
private final String name;
private final int protocol;
private final UUID uuid;
public BungeePlayerImpl(CachedPlayer player, int protocol){
this.name = player.name();
this.protocol = protocol;
this.uuid = player.uuid();
}
@Override
public String getName(){
return name;
}
@Override
public int getProtocol(){
return protocol;
}
@Override
public UUID getUUID(){
return uuid;
}
}
@@ -1,53 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2022-2024 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.bungeecord.objects.impl;
import ch.andre601.advancedserverlist.api.bungeecord.objects.BungeeProxy;
import net.md_5.bungee.api.config.ServerInfo;
import java.util.Map;
public record BungeeProxyImpl(Map<String, ServerInfo> servers, int playersOnline, int playersMax, String host) implements BungeeProxy{
@Override
public Map<String, ServerInfo> getServers(){
return servers;
}
@Override
public int getPlayersOnline(){
return playersOnline;
}
@Override
public int getPlayersMax(){
return playersMax;
}
@Override
public String getHost(){
return host;
}
}
@@ -1,53 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2022-2024 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.bungeecord.objects.placeholders;
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import java.util.Locale;
public class BungeePlayerPlaceholders extends PlaceholderProvider{
private BungeePlayerPlaceholders(){
super("player");
}
public static BungeePlayerPlaceholders init(){
return new BungeePlayerPlaceholders();
}
@Override
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
return switch(placeholder.toLowerCase(Locale.ROOT)){
case "name" -> player.getName();
case "protocol" -> String.valueOf(player.getProtocol());
case "uuid" -> String.valueOf(player.getUUID());
default -> null;
};
}
}
@@ -1,61 +0,0 @@
package ch.andre601.advancedserverlist.bungeecord.objects.placeholders;
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.chat.BaseComponent;
public class BungeeProxyPlaceholders extends PlaceholderProvider{
private final BungeeCordCore plugin;
private BungeeProxyPlaceholders(BungeeCordCore plugin){
super("proxy");
this.plugin = plugin;
}
public static BungeeProxyPlaceholders init(BungeeCordCore plugin){
return new BungeeProxyPlaceholders(plugin);
}
@Override
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
String[] args = placeholder.split("\\s", 2);
if(args.length < 2)
return null;
ServerPing ping = plugin.getFetchedServers().get(args[1]);
return switch(args[0]){
case "status" -> ping == null ? "offline" : "online";
case "motd" -> {
if(ping == null)
yield "none";
BaseComponent[] components = new BaseComponent[]{ping.getDescriptionComponent()};
Component component = BungeeComponentSerializer.get().deserialize(components);
yield ComponentParser.toMMString(component);
}
case "players" -> {
if(ping == null)
yield "0";
yield String.valueOf(ping.getPlayers().getOnline());
}
case "maxPlayers" -> {
if(ping == null)
yield "0";
yield String.valueOf(ping.getPlayers().getMax());
}
default -> null;
};
}
}
@@ -1,103 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2022-2024 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.bungeecord.objects.placeholders;
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
import ch.andre601.advancedserverlist.api.bungeecord.objects.BungeeProxy;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
import net.md_5.bungee.api.config.ServerInfo;
import java.net.InetSocketAddress;
public class BungeeServerPlaceholders extends PlaceholderProvider{
private final BungeeCordCore plugin;
private BungeeServerPlaceholders(BungeeCordCore plugin){
super("server");
this.plugin = plugin;
}
public static BungeeServerPlaceholders init(BungeeCordCore plugin){
return new BungeeServerPlaceholders(plugin);
}
@Override
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
if(!(server instanceof BungeeProxy proxy))
return null;
String[] args = placeholder.split("\\s", 2);
return switch(args[0]){
case "playersOnline" -> {
if(args.length >= 2){
String[] servers = args[1].split(",");
int players = 0;
for(String serverName : servers){
if(serverName.isEmpty())
continue;
ServerInfo info = proxy.getServers().get(serverName.strip());
if(info == null)
continue;
players += plugin.getOnlinePlayers(info);
}
yield String.valueOf(players);
}
yield String.valueOf(proxy.getPlayersOnline());
}
case "playersMax" -> {
// ServerInfo doesn't provide the max players that could join, so we won't allow an extra argument
if(args.length >= 2)
yield "";
yield String.valueOf(proxy.getPlayersMax());
}
case "host" -> {
if(args.length > 2)
yield "";
if(args.length == 2 && args[1] != null && !args[1].isEmpty()){
ServerInfo info = proxy.getServers().get(args[1]);
if(info == null)
yield "";
yield String.valueOf(((InetSocketAddress)info.getSocketAddress()).getHostString());
}
yield proxy.getHost();
}
default -> null;
};
}
}
-11
View File
@@ -1,11 +0,0 @@
name: 'AdvancedServerList'
author: 'Andre_601'
version: '${plugin.version}'
description: '${plugin.description}'
softDepends:
- PAPIProxyBridge
- Maintenance
- ServerListPlus
main: 'ch.andre601.advancedserverlist.bungeecord.BungeeCordCore'
@@ -1,28 +0,0 @@
{
"libraries": [
{
"groupId": "net{}kyori",
"artifactId": "adventure-api",
"version": "${dependency.adventure-api}",
"resolveTransitiveDependencies": true
},
{
"groupId": "net{}kyori",
"artifactId": "adventure-text-minimessage",
"version": "${dependency.adventure-text-minimessage}",
"resolveTransitiveDependencies": true
},
{
"groupId": "net{}kyori",
"artifactId": "adventure-platform-bungeecord",
"version": "${dependency.adventure-platform-bungeecord}",
"resolveTransitiveDependencies": true
},
{
"groupId": "org{}spongepowered",
"artifactId": "configurate-yaml",
"version": "${dependency.configurate-yaml}",
"resolveTransitiveDependencies": true
}
]
}
+50 -46
View File
@@ -39,9 +39,11 @@
<version>${plugin.version}</version>
<properties>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
<plugin.config.version>8</plugin.config.version>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<plugin.config.version>10</plugin.config.version>
</properties>
<repositories>
@@ -77,7 +79,7 @@
<dependency>
<groupId>ch.andre601</groupId>
<artifactId>expressionparser</artifactId>
<version>1.5.0</version>
<version>1.7.0-b1</version>
<scope>compile</scope>
</dependency>
@@ -91,7 +93,7 @@
<dependency>
<groupId>org.spongepowered</groupId>
<artifactId>configurate-hocon</artifactId>
<version>4.1.2</version>
<version>4.2.0</version>
<scope>provided</scope>
</dependency>
@@ -119,7 +121,7 @@
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.11.0</version>
<version>2.14.0</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -131,21 +133,48 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.4.0-jre</version>
<version>33.6.0-jre</version>
<scope>provided</scope>
</dependency>
<!-- Command Framework -->
<dependency>
<groupId>org.incendo</groupId>
<artifactId>cloud-annotations</artifactId>
<version>${dependency.cloud-annotations}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.incendo</groupId>
<artifactId>cloud-minecraft-extras</artifactId>
<version>${dependency.cloud-minecraft-extras}</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>net.kyori</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Annotations -->
<dependency>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>
<version>1.0.0</version>
</dependency>
<!-- Plugins -->
<dependency>
<groupId>net.william278</groupId>
<artifactId>papiproxybridge</artifactId>
<version>1.7.2</version>
<version>1.8.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>eu.kennytv.maintenance</groupId>
<artifactId>maintenance-api</artifactId>
<version>4.2.1</version>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -154,18 +183,24 @@
<version>3.5.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.github.miniplaceholders</groupId>
<artifactId>miniplaceholders-api</artifactId>
<version>3.2.0</version>
<scope>provided</scope>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.11.4</version>
<version>5.14.4</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>AdvancedServerList-Core-${plugin.version}</finalName>
<finalName>AdvancedServerList-Core-${plugin.version}${project.build.build_id}</finalName>
<resources>
<resource>
<filtering>true</filtering>
@@ -186,43 +221,12 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.0</version>
<version>3.15.0</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<relocations>
<relocation>
<pattern>com.unascribed</pattern>
<shadedPattern>ch.andre601.advancedserverlist.core.depends.flexver</shadedPattern>
</relocation>
<relocation>
<pattern>ch.andre601.expressionparser</pattern>
<shadedPattern>ch.andre601.advancedserverlist.core.depends.expressionparser</shadedPattern>
</relocation>
</relocations>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
@@ -28,30 +28,58 @@ package ch.andre601.advancedserverlist.core;
import ch.andre601.advancedserverlist.api.AdvancedServerListAPI;
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
import ch.andre601.advancedserverlist.core.check.UpdateChecker;
import ch.andre601.advancedserverlist.core.commands.CommandHandler;
import ch.andre601.advancedserverlist.core.commands.*;
import ch.andre601.advancedserverlist.core.compat.maintenance.MaintenancePlaceholder;
import ch.andre601.advancedserverlist.core.file.FileHandler;
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
import ch.andre601.advancedserverlist.core.parsing.TextCenterUtil;
import ch.andre601.advancedserverlist.core.profiles.conditions.ProfileConditionParser;
import ch.andre601.advancedserverlist.core.placeholders.DatePlaceholder;
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
import ch.andre601.advancedserverlist.core.profiles.conditions.reader.PlaceholderTokenReader;
import ch.andre601.advancedserverlist.core.profiles.conditions.reader.PlaceholderValueReader;
import ch.andre601.advancedserverlist.core.profiles.handlers.PlayerHandler;
import ch.andre601.expressionparser.DefaultExpressionParserEngine;
import org.incendo.cloud.CommandManager;
import org.incendo.cloud.annotations.AnnotationParser;
import org.incendo.cloud.description.Description;
import org.incendo.cloud.parser.ParserDescriptor;
import org.incendo.cloud.parser.standard.BooleanParser;
import org.incendo.cloud.parser.standard.IntegerParser;
import org.incendo.cloud.parser.standard.StringParser;
import org.incendo.cloud.permission.Permission;
import org.incendo.cloud.suggestion.Suggestion;
import org.incendo.cloud.suggestion.SuggestionProvider;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
public class AdvancedServerList<F>{
public static <F> AdvancedServerList<F> init(PluginCore<F> plugin, PlaceholderProvider... placeholders){
List<PlaceholderProvider> providers = new ArrayList<>(Arrays.asList(placeholders));
providers.add(new DatePlaceholder());
return new AdvancedServerList<>(plugin, providers);
}
private final PluginCore<F> plugin;
private final FileHandler fileHandler;
private final CommandHandler commandHandler;
private final PlayerHandler playerHandler;
private final TextCenterUtil textCenterUtil;
private static final AdvancedServerListAPI api = AdvancedServerListAPI.get();
private final ProfileConditionParser parser = ProfileConditionParser.create();
private final DefaultExpressionParserEngine parser = new DefaultExpressionParserEngine.DefaultBuilder()
.addDefaults()
.addTokenReader(new PlaceholderTokenReader())
.addValueReader(new PlaceholderValueReader())
.build();
private UpdateChecker updateChecker;
@@ -60,11 +88,10 @@ public class AdvancedServerList<F>{
private AdvancedServerList(PluginCore<F> plugin, List<PlaceholderProvider> placeholders){
this.plugin = plugin;
this.fileHandler = new FileHandler(this);
this.commandHandler = new CommandHandler(this);
this.playerHandler = new PlayerHandler(this);
this.textCenterUtil = new TextCenterUtil(this);
plugin.getPluginLogger().info("Registering internal Placeholders...");
plugin.pluginLogger().info("Registering internal Placeholders...");
placeholders.forEach(this::addPlaceholder);
@@ -73,60 +100,52 @@ public class AdvancedServerList<F>{
}
resolveVersion();
getPlugin().getPluginLogger().info("Starting <white>AdvancedServerList v%s</white>...", version);
plugin().pluginLogger().info("Starting <white>AdvancedServerList v%s</white>...", version);
load();
}
public static <F> AdvancedServerList<F> init(PluginCore<F> plugin, PlaceholderProvider... placeholders){
return new AdvancedServerList<>(plugin, Arrays.asList(placeholders));
}
public static AdvancedServerListAPI getApi(){
public static AdvancedServerListAPI api(){
return api;
}
public PluginCore<F> getPlugin(){
public PluginCore<F> plugin(){
return plugin;
}
public FileHandler getFileHandler(){
public FileHandler fileHandler(){
return fileHandler;
}
public CommandHandler getCommandHandler(){
return commandHandler;
}
public PlayerHandler getPlayerHandler(){
public PlayerHandler playerHandler(){
return playerHandler;
}
public String getVersion(){
public String version(){
return version;
}
public ProfileConditionParser getParser(){
public DefaultExpressionParserEngine parser(){
return parser;
}
public UpdateChecker getUpdateChecker(){
public UpdateChecker updateChecker(){
return updateChecker;
}
public TextCenterUtil getTextCenterUtil(){
public TextCenterUtil textCenterUtil(){
return textCenterUtil;
}
public void disable(){
getPlugin().getPluginLogger().info("[-] Saving <white>playercache.json</white> file...");
getPlayerHandler().save();
plugin().pluginLogger().info("[-] Saving <white>playercache.json</white> file...");
playerHandler().save();
if(updateChecker != null){
getPlugin().getPluginLogger().info("[-] Disabling Update Checker...");
plugin().pluginLogger().info("[-] Disabling Update Checker...");
updateChecker.disable();
}
getPlugin().getPluginLogger().success("AdvancedServerList disabled!");
plugin().pluginLogger().success("AdvancedServerList disabled!");
}
public void clearFaviconCache(){
@@ -134,68 +153,69 @@ public class AdvancedServerList<F>{
}
public void clearPlayerCache(){
getPlayerHandler().clearCache();
playerHandler().clearCache();
}
private void load(){
if(!getFileHandler().loadConfig()){
getPlugin().getPluginLogger().failure("Unable to load <white>config.yml</white>! Check previous lines for errors.");
if(!fileHandler().loadConfig()){
plugin().pluginLogger().failure("Unable to load <white>config.yml</white>! Check previous lines for errors.");
return;
}
if(getFileHandler().isOldConfig()){
getPlugin().getPluginLogger().info("[-] Detected old <white>config.yml</white>. Attempting to migrate...");
if(getFileHandler().migrateConfig()){
getPlugin().getPluginLogger().success("Migration completed successfully!");
if(fileHandler().isOldConfig()){
plugin().pluginLogger().info("[-] Detected old <white>config.yml</white>. Attempting to migrate...");
if(fileHandler().migrateConfig()){
plugin().pluginLogger().success("Migration completed successfully!");
}else{
getPlugin().getPluginLogger().failure("Couldn't migrate <white>config.yml</white>! Check previous lines for errors.");
plugin().pluginLogger().failure("Couldn't migrate <white>config.yml</white>! Check previous lines for errors.");
return;
}
}
if(getFileHandler().getBool(true, "printBanner")){
if(fileHandler().getBool(true, "printBanner")){
printBanner();
}
getPlugin().getPluginLogger().info("Platform: <white>%s</white>", plugin.getPlatformInfo());
getPlugin().getPluginLogger().info("");
plugin().pluginLogger().info("Platform: <white>%s</white>", plugin.platformInfo());
plugin().pluginLogger().info("");
if(getFileHandler().loadProfiles()){
getPlugin().getPluginLogger().success("Successfully loaded <white>%d</white> profile(s)!", getFileHandler().getProfiles().size());
if(fileHandler().loadProfiles()){
plugin().pluginLogger().success("Successfully loaded <white>%d</white> profile(s)!", fileHandler().profiles().size());
}else{
getPlugin().getPluginLogger().failure("Unable to load profiles! Check previous lines for errors.");
plugin().pluginLogger().failure("Unable to load profiles! Check previous lines for errors.");
return;
}
getPlugin().loadFaviconHandler(this);
plugin.loadCommands();
loadCommands();
plugin.loadEvents();
getPlayerHandler().load();
playerHandler().load();
plugin.loadMetrics();
plugin.startScheduler();
getPlugin().getPluginLogger().success("<green>AdvancedServerList is ready!");
plugin().pluginLogger().success("<green>AdvancedServerList is ready!");
if(getFileHandler().getBoolean("checkUpdates"))
if(fileHandler().getBoolean("checkUpdates"))
this.updateChecker = new UpdateChecker(this);
}
private void printBanner(){
getPlugin().getPluginLogger().info("<#3b90ff> __ _______ ___");
getPlugin().getPluginLogger().info("<#3b90ff> /\\ \\ / ____|_| | |");
getPlugin().getPluginLogger().info("<#3b90ff> / \\ \\ | (_(___ | | |");
getPlugin().getPluginLogger().info("<#3b90ff> / /\\ \\ \\ \\___ \\ \\| | |");
getPlugin().getPluginLogger().info("<#3b90ff> / ____ \\ \\ ____) | | |_|____");
getPlugin().getPluginLogger().info("<#3b90ff>/_/_/ \\_\\_\\_____/_/|______|_|");
getPlugin().getPluginLogger().info("");
plugin().pluginLogger().info("<#3b90ff> __ _______ ___");
plugin().pluginLogger().info("<#3b90ff> /\\ \\ / ____|_| | |");
plugin().pluginLogger().info("<#3b90ff> / \\ \\ | (_(___ | | |");
plugin().pluginLogger().info("<#3b90ff> / /\\ \\ \\ \\___ \\ \\| | |");
plugin().pluginLogger().info("<#3b90ff> / ____ \\ \\ ____) | | |_|____");
plugin().pluginLogger().info("<#3b90ff>/_/_/ \\_\\_\\_____/_/|______|_|");
plugin().pluginLogger().info("");
seasonalText();
}
private void resolveVersion(){
try(InputStream is = getClass().getResourceAsStream("/version.properties")){
try(InputStream stream = getClass().getResourceAsStream("/version.properties")){
Properties properties = new Properties();
properties.load(is);
properties.load(stream);
version = properties.getProperty("version");
}catch(IOException ex){
@@ -204,6 +224,124 @@ public class AdvancedServerList<F>{
}
private void addPlaceholder(PlaceholderProvider placeholderProvider){
AdvancedServerList.getApi().addPlaceholderProvider(placeholderProvider);
AdvancedServerList.api().addPlaceholderProvider(placeholderProvider);
}
private void loadCommands(){
plugin().loadFaviconHandler(this);
CommandManager<CmdSender> commandManager = plugin().commandManager();
commandManager.registerCommandPostProcessor(new CommandTypePostprocessor());
AnnotationParser<CmdSender> annotationParser = new AnnotationParser<>(commandManager, CmdSender.class);
annotationParser.registerBuilderModifier(
CommandType.class,
(annotation, builder) -> builder.meta(CommandHandler.COMMAND_TYPE, annotation.value())
);
annotationParser.parse(
CmdClearCache.create(this),
CmdHelp.create(commandManager),
CmdMigrate.create(this),
CmdProfiles.create(this),
CmdReload.create(this)
);
for(String option : CmdProfiles.ProfileOption.list()){
commandManager.command(
commandManager.commandBuilder("advancedserverlist", "asl")
.literal("profiles")
.literal("set")
.required(
"profile",
StringParser.stringParser(),
Description.of("The profile to edit."),
SuggestionProvider.suggesting(
fileHandler().profiles().stream()
.map(ServerListProfile::file)
.map(Suggestion::suggestion)
.toList()
)
)
.literal(option)
.optional("value", descriptor(option), description(option))
.commandDescription(Description.of("Sets or resets profile values."))
.handler(context -> CommandHandler.handleSet(this, context))
.meta(CommandHandler.OPTION, option)
.permission(
Permission.anyOf(
Permission.permission("advancedserverlist.admin"),
Permission.permission("advancedserverlist.command.profiles"),
Permission.permission("advancedserverlist.command.profiles.set")
)
)
.meta(CommandHandler.COMMAND_TYPE, CommandType.Type.ALL)
);
}
plugin().pluginLogger().success("Loaded Command <white>/advancedserverlist</white>!");
}
private ParserDescriptor<CmdSender, ?> descriptor(String option){
return switch(option){
case "playercount.hideplayers", "playercount.hideplayershover", "playercount.extraplayers.enabled",
"playercount.maxplayers.enabled", "playercount.onlineplayers.enabled" -> BooleanParser.booleanParser();
case "priority" -> IntegerParser.integerParser();
default -> StringParser.greedyStringParser();
};
}
private Description description(String option){
return switch(option){
case "playercount.hideplayers", "playercount.hideplayershover", "playercount.extraplayers.enabled",
"playercount.maxplayers.enabled", "playercount.onlineplayers.enabled" -> Description.of(
"Whether this option should be enabled or not. Leave empty to unset it."
);
case "priority" -> Description.of("Priority this Profile should have. Leave empty to reset.");
case "motd", "playercount.hover" -> Description.of(
"Lines to set. Use \\n or <newline> to set a linebreak. Leave empty to reset."
);
default -> Description.of("The Value to use for this option. Leave empty to reset.");
};
}
// Adding some seasonal messages
private void seasonalText(){
LocalDate date = LocalDate.now();
switch(date.getMonth()){
case JANUARY -> {
if(date.getDayOfMonth() == 1){
plugin().pluginLogger().info("Happy new Year!");
plugin().pluginLogger().info("");
}
}
case JUNE -> {
plugin().pluginLogger().info("Happy Pride Month!");
plugin().pluginLogger().info("");
}
case AUGUST -> {
if(date.getDayOfMonth() == 1){
plugin().pluginLogger().info("Happy Birthday Switzerland!");
plugin().pluginLogger().info("");
}
}
case OCTOBER -> {
if(date.getDayOfMonth() == 31){
plugin().pluginLogger().info("Happy Halloween!");
plugin().pluginLogger().info("");
}
}
case DECEMBER -> {
if(date.getDayOfMonth() == 13){
plugin().pluginLogger().info("Happy Birthday Andre_601!");
plugin().pluginLogger().info("");
}else
if(date.getDayOfMonth() > 23 && date.getDayOfMonth() < 27){
plugin().pluginLogger().info("Merry Christmas!");
plugin().pluginLogger().info("");
}
}
}
}
}
@@ -25,13 +25,13 @@
package ch.andre601.advancedserverlist.core.check;
import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.NonNull;
import java.util.concurrent.ThreadFactory;
public class UpdateCheckThread implements ThreadFactory{
@Override
public Thread newThread(@NotNull Runnable r){
public Thread newThread(@NonNull Runnable r){
Thread t = new Thread(r, "AdvancedServerList-UpdateThread");
t.setDaemon(true);
return t;
@@ -68,8 +68,8 @@ public class UpdateChecker{
public UpdateChecker(AdvancedServerList<?> core){
this.core = core;
this.logger = core.getPlugin().getPluginLogger();
this.loader = core.getPlugin().getLoader();
this.logger = core.plugin().pluginLogger();
this.loader = core.plugin().loader();
this.client = HttpClient.newBuilder().executor(Executors.newSingleThreadExecutor(new UpdateCheckThread())).build();
startUpdateChecker();
@@ -88,10 +88,10 @@ public class UpdateChecker{
if(version == null)
return;
int result = version.compare(core.getVersion());
int result = version.compare(core.version());
if(result == -1){
sender.sendPrefixedMsg("<green>A new Version of AdvancedServerList is available!");
sender.sendPrefixedMsg("<green>Your version: <white>%s", core.getVersion());
sender.sendPrefixedMsg("<green>Your version: <white>%s", core.version());
sender.sendPrefixedMsg("<green>Version on Modrinth: <white>%s", version.versionNumber());
sender.sendPrefixedMsg(
"<click:open_url:'https://modrinth.com/plugin/advancedserverlist/version/%s'>" +
@@ -101,8 +101,12 @@ public class UpdateChecker{
);
}else
if(result == 1){
sender.sendPrefixedMsg("You seem to run a newer Version (<white>%</white>) than what is available on Modrinth (<white>%s</white>).", core.getVersion(), version.versionNumber());
sender.sendPrefixedMsg("Are you running a development build?");
sender.sendPrefixedMsg(
"Your version (<white>%</white>) is newer than what is available on Modrinth (<white>%s</white>).",
core.version(),
version.versionNumber()
);
sender.sendPrefixedMsg("Are you using a Development Version?");
}
});
}
@@ -133,14 +137,18 @@ public class UpdateChecker{
logger.debug(UpdateChecker.class, "Comparing versions...");
int result = version.compare(core.getVersion());
int result = version.compare(core.version());
switch(result){
case -1 -> printUpdateBanner(version);
case 0 -> logger.success("You are running the latest version!");
case 1 -> logger.info(
"[?] Your version (<white>%s</white>) seems to be newer than the latest release (<white>%s</white>). Are you running a dev build?",
core.getVersion(), version.versionNumber()
);
case 1 -> {
logger.info(
"[?] Your version (<white>%s</white>) is newer than what is available on Modrint (<white>%s</white>).",
core.version(),
version.versionNumber()
);
logger.info(" Are you using a Development Version?");
}
}
});
}, 0L, 12L, TimeUnit.HOURS);
@@ -155,7 +163,7 @@ public class UpdateChecker{
logger.debug(UpdateChecker.class, "Checking '%s' for updates...", finalUrl);
if(core.getVersion().equals("UNKNOWN")){
if(core.version().equals("UNKNOWN")){
logger.warn("Cannot perform update check. Plugin version is 'UNKNOWN'!");
return CompletableFuture.completedFuture(null);
}
@@ -165,7 +173,7 @@ public class UpdateChecker{
request = HttpRequest.newBuilder()
.uri(new URI(finalUrl))
.timeout(Duration.ofSeconds(5))
.header("User-Agent", "AdvancedServerList-" + loader + "/" + core.getVersion())
.header("User-Agent", "AdvancedServerList-" + loader + "/" + core.version())
.build();
}catch(URISyntaxException ex){
logger.warn("Cannot perform update check. URL '%s' is not a valid URI.", ex, finalUrl);
@@ -185,7 +193,7 @@ public class UpdateChecker{
logger.warn("=======================================================================");
logger.warn("You are running an outdated version of AdvancedServerList!");
logger.warn("");
logger.warn("Your version: <white>%s</white>", core.getVersion());
logger.warn("Your version: <white>%s</white>", core.version());
logger.warn("Modrinth: <white>%s</white>", version.versionNumber());
logger.warn("");
@@ -0,0 +1,54 @@
package ch.andre601.advancedserverlist.core.commands;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
import org.incendo.cloud.annotations.Argument;
import org.incendo.cloud.annotations.Command;
import org.incendo.cloud.annotations.CommandDescription;
import org.incendo.cloud.annotations.Permission;
import org.jspecify.annotations.Nullable;
@Command("advancedserverlist|asl")
@CommandDescription("Main Command of the plugin")
@Permission("advancedserverlist.admin")
@CommandType(CommandType.Type.ALL)
public record CmdClearCache(AdvancedServerList<?> core){
public static CmdClearCache create(AdvancedServerList<?> core){
return new CmdClearCache(core);
}
@Command("clearcache [target]")
@CommandDescription("Cleares the Favicon and/or Player Cache.")
@Permission({"advancedserverlist.admin", "advancedserverlist.command.clearcache"})
@CommandType(CommandType.Type.ALL)
public void handle(
CmdSender sender,
@Nullable @Argument(description = "The type of Cache to clean. Defaults to ALL.") CacheTarget target
){
if(target == null)
target = CacheTarget.ALL;
sender.sendPrefixedMsg("Clearing cache...");
if(target == CacheTarget.ALL || target == CacheTarget.FAVICON){
core().clearFaviconCache();
sender.sendPrefixedMsg("Cleared Favicon Cache.");
}
if(target == CacheTarget.ALL || target == CacheTarget.PLAYER){
core().clearPlayerCache();
sender.sendPrefixedMsg("Cleared Player Cache.");
}
sender.sendPrefixedMsg("<green>Cache clearing complete!");
}
public enum CacheTarget{
ALL,
FAVICON,
PLAYER
}
}
@@ -0,0 +1,65 @@
package ch.andre601.advancedserverlist.core.commands;
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
import net.kyori.adventure.text.format.NamedTextColor;
import org.incendo.cloud.CommandManager;
import org.incendo.cloud.annotation.specifier.Greedy;
import org.incendo.cloud.annotations.Argument;
import org.incendo.cloud.annotations.Command;
import org.incendo.cloud.annotations.CommandDescription;
import org.incendo.cloud.annotations.Permission;
import org.incendo.cloud.annotations.suggestion.Suggestions;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.help.result.CommandEntry;
import org.incendo.cloud.minecraft.extras.MinecraftHelp;
import org.jspecify.annotations.Nullable;
import java.util.List;
@Command("advancedserverlist|asl")
@CommandDescription("Main Command of the plugin")
@Permission("advancedserverlist.admin")
@CommandType(CommandType.Type.ALL)
public record CmdHelp(CommandManager<CmdSender> manager, MinecraftHelp<CmdSender> help){
public static CmdHelp create(CommandManager<CmdSender> manager){
MinecraftHelp<CmdSender> help = MinecraftHelp.<CmdSender>builder()
.commandManager(manager)
.audienceProvider(CmdSender::audience)
.commandPrefix("/asl help")
.colors(MinecraftHelp.helpColors(
NamedTextColor.WHITE, NamedTextColor.AQUA, NamedTextColor.WHITE, NamedTextColor.GRAY, NamedTextColor.DARK_GRAY
))
.build();
return new CmdHelp(manager, help);
}
@Command("help [query]")
@CommandDescription("Displays all commands or provides info about a specific one.")
@Permission({"advancedserverlist.admin", "advancedserverlist.command.help"})
@CommandType(CommandType.Type.ALL)
public void handle(
CmdSender sender,
@Nullable @Argument(description = "Search Query for a specific command", suggestions = "help") @Greedy String query
){
this.help.queryCommands(query == null ? "" : query, sender);
}
@Suggestions("help")
public List<String> helpQueries(CommandContext<CmdSender> context) {
return this.manager.createHelpHandler()
.queryRootIndex(context.sender())
.entries()
.stream()
.filter(entry -> context.hasPermission(entry.command().commandPermission()))
.filter(entry -> {
CommandType.Type type = entry.command().commandMeta().getOrDefault(CommandHandler.COMMAND_TYPE, CommandType.Type.ALL);
return type == CommandType.Type.ALL || type == CommandType.Type.PLAYER_ONLY && context.sender().isPlayer();
})
.map(CommandEntry::syntax)
.toList();
}
}
@@ -0,0 +1,78 @@
package ch.andre601.advancedserverlist.core.commands;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
import ch.andre601.advancedserverlist.core.migration.minimotd.MiniMOTDConfigMigrator;
import ch.andre601.advancedserverlist.core.migration.serverlistplus.SLPConfigMigrator;
import org.incendo.cloud.annotations.Argument;
import org.incendo.cloud.annotations.Command;
import org.incendo.cloud.annotations.CommandDescription;
import org.incendo.cloud.annotations.Permission;
import org.jspecify.annotations.NonNull;
import java.util.Locale;
@Command("advancedserverlist|asl")
@CommandDescription("Main Command of the plugin")
@Permission("advancedserverlist.admin")
@CommandType(CommandType.Type.ALL)
public record CmdMigrate(AdvancedServerList<?> core){
public static CmdMigrate create(AdvancedServerList<?> core){
return new CmdMigrate(core);
}
@Command("migrate <plugin>")
@CommandDescription("Migrates Profiles from supported Plugins to ASL.")
@Permission({"advancedserverlist.admin", "advancedserverlist.command.migrate"})
@CommandType(CommandType.Type.ALL)
public void handle(
CmdSender sender,
@NonNull @Argument(description = "Name of the plugin to migrate from") MigratablePlugin plugin
){
if(plugin == MigratablePlugin.SERVERLISTPLUS){
if(!core().plugin().isPluginEnabled("ServerListPlus")){
sender.sendErrorMsg("<red>ServerListPlus not enabled.");
sender.sendErrorMsg("<red>The plugin needs to be enabled for the migration to work.");
return;
}
sender.sendPrefixedMsg("Migrating from ServerListPlus...");
int migrated = SLPConfigMigrator.migrate(core(), sender);
if(migrated == 0){
sender.sendErrorMsg("<red>Couldn't migrate any profiles from ServerListPlus.");
sender.sendErrorMsg("<red>See the server console for any errors.");
return;
}else{
sender.sendPrefixedMsg("<green>Successfully migrated </green>%d<green> Profile(s)!", migrated);
}
}else
if(plugin == MigratablePlugin.MINIMOTD){
sender.sendPrefixedMsg("Migrating from MiniMOTD...");
if(MiniMOTDConfigMigrator.migrate(core(), sender)){
sender.sendPrefixedMsg("<green>Successfully migrated config from MiniMOTD!");
}else{
sender.sendErrorMsg("<red>Couldn't migrate config from MiniMOTD.");
sender.sendErrorMsg("<red>See the server console for any errors.");
return;
}
}else{
sender.sendErrorMsg("<red>Received invalid Plugin </red>%s<red>!", plugin.toString());
sender.sendErrorMsg("<red>Only the following plugins are supported:");
for(MigratablePlugin pl : MigratablePlugin.values()){
sender.sendErrorMsg("- <click:suggest_command:/asl migrate %s>%s</click>", pl.name().toLowerCase(Locale.ROOT), pl.name().toLowerCase(Locale.ROOT));
}
}
sender.sendPrefixedMsg("<green>Run <white><click:suggest_command:/asl reload>/asl reload</click></white> to load the new Profile(s).");
}
public enum MigratablePlugin{
SERVERLISTPLUS,
MINIMOTD
}
}
@@ -0,0 +1,483 @@
package ch.andre601.advancedserverlist.core.commands;
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
import ch.andre601.advancedserverlist.core.interfaces.dialogs.IDialog;
import ch.andre601.advancedserverlist.core.interfaces.dialogs.IDialogList;
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
import ch.andre601.advancedserverlist.core.profiles.profile.ProfileSerializer;
import org.incendo.cloud.annotations.Argument;
import org.incendo.cloud.annotations.Command;
import org.incendo.cloud.annotations.CommandDescription;
import org.incendo.cloud.annotations.Permission;
import org.incendo.cloud.annotations.suggestion.Suggestions;
import org.incendo.cloud.context.CommandInput;
import org.jspecify.annotations.NonNull;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.serialize.SerializationException;
import org.spongepowered.configurate.yaml.NodeStyle;
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
@Command("advancedserverlist|asl")
@CommandDescription("Main Command of the plugin.")
@Permission("advancedserverlist.admin")
@CommandType(CommandType.Type.ALL)
public record CmdProfiles(AdvancedServerList<?> core){
public static CmdProfiles create(AdvancedServerList<?> core){
return new CmdProfiles(core);
}
@Command("profiles add <name>")
@CommandDescription("Creates a new Profile with default values applied.")
@Permission({"advancedserverlist.admin", "advancedserverlist.command.profiles", "advancedserverlist.command.profiles.add"})
@CommandType(CommandType.Type.ALL)
public void add(
CmdSender sender,
@NonNull @Argument(description = "Name of the profile to create.") String name
){
if(name.isEmpty()){
sender.sendErrorMsg("<red>Insufficient arguments.");
sender.sendErrorMsg("<red>A profile name needs to be provided.");
return;
}
if(CommandHandler.profile(core(), name) != null){
sender.sendErrorMsg("<red>A profile with the name </red>%s<red> already exists.", name);
return;
}
String file = CommandHandler.profileName(name);
if(core().fileHandler().createFile(file)){
sender.sendPrefixedMsg("<green>Successfully created </green>%s<green>!", file);
sender.sendPrefixedMsg("<green>Use <white><click:suggest_command:/asl reload>/asl reload</click></white> to load the file.");
}else{
sender.sendErrorMsg("<red>Error while creating new profile.");
sender.sendErrorMsg("<red>Check the server console for details.");
}
}
@Command("profiles copy <profile> <name>")
public void copy(
CmdSender sender,
@NonNull @Argument(description = "Name of the profile to copy.", suggestions = "profiles") String profile,
@NonNull @Argument(description = "Name of the copy to have.") String name
){
if(profile.isEmpty() || name.isEmpty()){
sender.sendErrorMsg("<red>Insufficient arguments.");
sender.sendErrorMsg("<red>A profile name and name for the copy need to be provided.");
return;
}
String profileName = CommandHandler.profileName(profile);
String copyName = CommandHandler.profileName(name);
ServerListProfile original = CommandHandler.profile(core(), profileName);
if(original == null){
sender.sendErrorMsg("<red>A profile with name </red>%s<red> does not exist.", profile);
return;
}
boolean copyExists = CommandHandler.profile(core(), copyName) != null;
if(copyExists){
sender.sendErrorMsg("<red>A profile with name </red>%s<red> already exists.", name);
return;
}
Path profilePath = core().plugin().folderPath().resolve("profiles").resolve(copyName);
if(!Files.exists(profilePath)){
try{
Files.createFile(profilePath);
}catch(IOException ex){
sender.sendErrorMsg("<red>Encountered an IOException while creating </red>%s<red>.", copyName);
sender.sendErrorMsg("<red>Please see the server console for further details.");
core.plugin().pluginLogger().warn("Encountered IOException while creating file <white>%s</white>", ex, copyName);
return;
}
YamlConfigurationLoader loader = YamlConfigurationLoader.builder()
.path(profilePath)
.indent(2)
.nodeStyle(NodeStyle.BLOCK)
.defaultOptions(opt -> opt.serializers(builder -> builder.register(ProfileEntry.class, ProfileSerializer.INSTANCE)))
.build();
ConfigurationNode node;
try{
node = loader.load();
}catch(IOException ex){
sender.sendErrorMsg("<red>Encountered an IOException while loading file </red>%s<red>.", copyName);
sender.sendErrorMsg("<red>Please see the server console for further details.");
core().plugin().pluginLogger().warn("Encountered IOException while loading file <white>%s</white>.", ex, copyName);
return;
}
try{
node.node("priority").set(original.priority());
node.node("condition").set(original.condition());
node.node("profiles").setList(ProfileEntry.class, original.profiles());
node.set(ProfileEntry.class, original.defaultProfile());
}catch(SerializationException ex){
sender.sendErrorMsg("<red>Encountered a SerializationException while copying values from </red>%s<red> to </red>%s<red>.", profileName, copyName);
sender.sendErrorMsg("<red>Please see the server console for further details.");
core().plugin().pluginLogger().warn("<red>Encountered a SerializationException while copying values from </red>%s<red> to </red>%s<red>.", ex, profileName, copyName);
return;
}
try{
loader.save(node);
sender.sendPrefixedMsg("<green>Successfully created copy of </green>%s<green> as </green>%s<green>!", profileName, copyName);
sender.sendPrefixedMsg("<green>Use <white><click:suggest_command:/asl reload>/asl reload</click></white> to load the file.");
}catch(IOException ex){
sender.sendErrorMsg("<red>There was an IOException while loading file </red>%s<red>!", copyName);
sender.sendErrorMsg("<red>Please see the server console for further details.");
core.plugin().pluginLogger().warn("Encountered an IOException while loading file <white>%s</white>", ex, copyName);
}
}
}
@Command("profiles edit <profile>")
@CommandDescription("Opens a Dialog to edit a specific profile ingame.")
@Permission({"advancedserverlist.admin", "advancedserverlist.command.profiles", "advancedserverlist.command.profiles.edit"})
@CommandType(CommandType.Type.PLAYER_ONLY)
public void edit(
CmdSender sender,
@NonNull @Argument(description = "Name of the profile to edit.", suggestions = "profiles") String profile
){
if(!core().plugin().isDialogsSupported()){
sender.sendErrorMsg("Dialogs are not supported on this server or proxy version.");
return;
}
ServerListProfile slp = CommandHandler.profile(core(), profile);
if(slp == null){
sender.sendErrorMsg("<red>A profile with name </red>%s<red> does not exist.", profile);
return;
}
IDialogList.IDialogListBuilder listBuilder = core().plugin().createDialogListBuilder(slp.file());
String warning = """
<gold><bold>WARNING!</bold></gold>
Dialog-based Profile editing is currently an <italic>experimental feature</italic>!
Please create backups of your profiles before using this feature and report any bugs and/or glitches you encounter.""";
listBuilder.appendBody(warning);
listBuilder.appendDialog(
createDialog("Main Settings", warning, slp, slp.defaultProfile(), slp.priority(), slp.condition(), -1)
);
for(int i = 0; i < slp.profiles().size(); i++){
listBuilder.appendDialog(
createDialog("Profiles #" + (i + 1), warning, slp, slp.profiles().get(i), -1, null, i)
);
}
listBuilder.build().show(sender);
}
@Command("profiles info <profile>")
@CommandDescription("Provides info about a specific profile.")
@Permission({"advancedserverlist.admin", "advancedserverlist.command.profiles", "advancedserverlist.command.profiles.info"})
@CommandType(CommandType.Type.PLAYER_ONLY)
public void info(
CmdSender sender,
@NonNull @Argument(description = "Name of the profile to view.", suggestions = "profiles") String profile
){
if(profile.isEmpty()){
sender.sendErrorMsg("<red>Insufficient arguments.");
sender.sendErrorMsg("<red>A profile name needs to be provided.");
return;
}
ServerListProfile slp = CommandHandler.profile(core(), profile);
if(slp == null){
sender.sendErrorMsg("<red>Unable to find profile with name </red>%s<red>.", profile);
sender.sendErrorMsg("<red>Make sure you typed it correctly.");
return;
}
ProfileOptionsSender optionsSender = new ProfileOptionsSender(slp.file());
optionsSender
.append(
ProfileOptionsSender.OptionStringBuilder.of("Priority", slp.priority())
.option(ProfileOption.PRIORITY)
).append(
ProfileOptionsSender.OptionStringBuilder.of("Condition", slp.condition())
.option(ProfileOption.CONDITION)
).append(
ProfileOptionsSender.OptionStringBuilder.of("Profiles")
.color(slp.profiles().isEmpty() ? ProfileOptionsSender.ColorName.RED : ProfileOptionsSender.ColorName.GREEN)
);
if(!slp.profiles().isEmpty()){
for(int i = 0; i < slp.profiles().size(); i++){
optionsSender.append(
ProfileOptionsSender.OptionStringBuilder.of("#" + (i + 1), profileHover(slp.profiles().get(i)))
.prefix("│ %s─".formatted((i + 1) == slp.profiles().size() ? "└" : "├"))
.color(ProfileOptionsSender.ColorName.AQUA)
);
}
}
ProfileEntry entry = slp.defaultProfile();
optionsSender
.append(
ProfileOptionsSender.OptionStringBuilder.of("MOTD", entry.motd())
.option(ProfileOption.MOTD)
).append(
ProfileOptionsSender.OptionStringBuilder.of("Favicon", entry.favicon())
.option(ProfileOption.FAVICON)
).append(
ProfileOptionsSender.OptionStringBuilder.of("Player count")
.prefix("└─")
.color(ProfileOptionsSender.ColorName.AQUA)
).append(
ProfileOptionsSender.OptionStringBuilder.of("Hide Player count?", entry.hidePlayersEnabled())
.prefix(" ├─")
.option(ProfileOption.PLAYERCOUNT_HIDEPLAYERS)
).append(
ProfileOptionsSender.OptionStringBuilder.of("Hide Player count hover?", entry.hidePlayersHoverEnabled())
.prefix(" ├─")
.option(ProfileOption.PLAYERCOUNT_HIDEPLAYERSHOVER)
).append(
ProfileOptionsSender.OptionStringBuilder.of("Player count hover", entry.players())
.prefix(" ├─")
.option(ProfileOption.PLAYERCOUNT_HOVER)
).append(
ProfileOptionsSender.OptionStringBuilder.of("Player count text", entry.playerCountText())
.prefix(" ├─")
.option(ProfileOption.PLAYERCOUNT_TEXT)
).append(
ProfileOptionsSender.OptionStringBuilder.of("Extra Players")
.prefix(" ├─")
.color(ProfileOptionsSender.ColorName.AQUA)
).append(
ProfileOptionsSender.OptionStringBuilder.of("Enabled?", entry.extraPlayersEnabled())
.prefix(" │ ├─")
.option(ProfileOption.PLAYERCOUNT_EXTRAPLAYERS_ENABLED)
).append(
ProfileOptionsSender.OptionStringBuilder.of("Amount", entry.extraPlayersCount())
.prefix(" │ └─")
.option(ProfileOption.PLAYERCOUNT_EXTRAPLAYERS_AMOUNT)
).append(
ProfileOptionsSender.OptionStringBuilder.of("Max Players")
.prefix(" ├─")
.color(ProfileOptionsSender.ColorName.AQUA)
).append(
ProfileOptionsSender.OptionStringBuilder.of("Enabled?", entry.maxPlayersEnabled())
.prefix(" │ ├─")
.option(ProfileOption.PLAYERCOUNT_MAXPLAYERS_ENABLED)
).append(
ProfileOptionsSender.OptionStringBuilder.of("Amount", entry.maxPlayersCount())
.prefix(" │ └─")
.option(ProfileOption.PLAYERCOUNT_MAXPLAYERS_AMOUNT)
).append(
ProfileOptionsSender.OptionStringBuilder.of("Online Players")
.prefix(" └─")
.color(ProfileOptionsSender.ColorName.AQUA)
).append(
ProfileOptionsSender.OptionStringBuilder.of("Enabled?", entry.onlinePlayersEnabled())
.prefix(" ├─")
.option(ProfileOption.PLAYERCOUNT_ONLINEPLAYERS_ENABLED)
).append(
ProfileOptionsSender.OptionStringBuilder.of("Amount", entry.onlinePlayersCount())
.prefix(" └─")
.option(ProfileOption.PLAYERCOUNT_ONLINEPLAYERS_AMOUNT)
).send(sender);
}
@Command("profiles list")
@CommandDescription("Lists all available profiles with priority, conditions and if they are valid.")
@Permission({"advancedserverlist.admin", "advancedserverlist.command.profiles", "advancedserverlist.command.profiles.list"})
@CommandType(CommandType.Type.ALL)
public void list(CmdSender sender){
sender.sendMsg();
sender.sendPrefixedMsg("Available Profiles:");
sender.sendMsg("<dark_grey>|");
List<ServerListProfile> profiles = core().fileHandler().profiles().stream()
.sorted(Comparator.comparingInt(ServerListProfile::priority).reversed())
.toList();
if(profiles.isEmpty()){
sender.sendMsg("<dark_grey></dark_grey> <i>No profiles found.</i>");
}else{
for(int i = 0; i < profiles.size(); i++){
sender.sendMsg(
"<dark_grey>%s</dark_grey> <aqua>" +
"<hover:show_text:\"%s\">" +
"<click:suggest_command:/asl profiles info %s>" +
"%s" +
"</click>" +
"</hover>" +
"</aqua>",
(i + 1) == profiles.size() ? "" : "-",
hover(profiles.get(i)),
profiles.get(i).file(),
profiles.get(i).file()
);
}
}
sender.sendMsg();
sender.sendMsg("<grey><i>Hover</i></grey> = Show details.");
sender.sendMsg("<grey><i>Click</i></grey> = Get Command.");
}
@Suggestions("profiles")
public List<String> profilesSuggestions(CommandInput input){
String name = input.readString();
List<String> profiles = core().fileHandler().profiles().stream()
.map(profile -> profile.file().substring(0, profile.file().lastIndexOf('.')))
.toList();
if(name.isEmpty())
return profiles;
List<String> matches = new ArrayList<>(profiles.size());
for(String str : profiles){
if(str.length() >= name.length() && str.regionMatches(true, 0, name, 0, name.length()))
matches.add(str);
}
return matches;
}
private String hover(ServerListProfile profile){
return """
<grey>Priority: <white>%d</white>
Condition: <white>%s</white>
Valid? %s""".formatted(
profile.priority(),
profile.condition() == null || profile.condition().isEmpty() ? "<i>None</i>" : profile.condition(),
profile.isInvalidProfile() ? "<red>No</red>" : "<green>Yes</green>"
);
}
private String profileHover(ProfileEntry entry){
return """
<grey>MOTD:
%s<reset>
<grey>Favicon: %s<reset>
<grey>Hide Player count? %s
Hide Player count Hover? %s
Player count Hover:
%s<reset>
<grey>Player count Text: %s<reset>
<grey>Extra Players: %s<reset>
<grey>Max Players: %s<reset>
<grey>Online Players: %s""".formatted(
entry.motd() == null || entry.motd().isEmpty() ?
"<i><white>None</white></i>" : String.join("<reset>\n", entry.motd()),
entry.favicon() == null || entry.favicon().isEmpty() ?
"<i><white>None</white></i>" : entry.favicon(),
entry.hidePlayersEnabled().getOrDefault(false) ? "<green>Enabled</green>" : "<red>Disabled</red>",
entry.hidePlayersHoverEnabled().getOrDefault(false) ? "<green>Enabled</green>" : "<red>Disabled</red>",
entry.players().isEmpty() ? "<i><white>None</white></i>" : String.join("<reset>\n", entry.players()),
entry.playerCountText() == null || entry.playerCountText().isEmpty() ?
"<i><white>None</white></i>" : entry.playerCountText(),
entry.extraPlayersEnabled().getOrDefault(false) ? entry.extraPlayersCount() : "<red>Disabled</red>",
entry.maxPlayersEnabled().getOrDefault(false) ? entry.maxPlayersCount() : "<red>Disabled</red>",
entry.onlinePlayersEnabled().getOrDefault(false) ? entry.onlinePlayersCount() : "<red>Disabled</red>"
);
}
private IDialog<?> createDialog(
String title,
String warning,
ServerListProfile slp,
ProfileEntry entry,
int priority,
String condition,
int index
){
IDialog.IDialogBuilder builder = core.plugin().createDialogBuilder(title, slp, index);
builder.appendBody(warning);
if(index < 0){
builder.appendNumberRange(ProfileOption.PRIORITY, "Priority", priority)
.appendTextInput(ProfileOption.CONDITION, "Condition", condition, 100);
}
builder.appendTextbox(ProfileOption.MOTD, "MOTD", entry.motd(), 200, 2)
.appendTextInput(ProfileOption.FAVICON, "Favicon", entry.favicon(), 100)
.appendBoolean(ProfileOption.PLAYERCOUNT_HIDEPLAYERS, "Hide Player Count", entry.hidePlayersEnabled())
.appendBoolean(ProfileOption.PLAYERCOUNT_HIDEPLAYERSHOVER, "Hide Player Count Hover", entry.hidePlayersHoverEnabled())
.appendTextbox(ProfileOption.PLAYERCOUNT_HOVER, "Player Count Hover", entry.players(), 200, 10)
.appendTextInput(ProfileOption.PLAYERCOUNT_TEXT, "Player Count Text", entry.playerCountText(), 100)
.appendBoolean(ProfileOption.PLAYERCOUNT_EXTRAPLAYERS_ENABLED, "Enable Extra Players", entry.extraPlayersEnabled())
.appendTextInput(ProfileOption.PLAYERCOUNT_EXTRAPLAYERS_AMOUNT, "Extra Players Count", entry.extraPlayersCount(), 100)
.appendBoolean(ProfileOption.PLAYERCOUNT_MAXPLAYERS_ENABLED, "Enable Max Players", entry.maxPlayersEnabled())
.appendTextInput(ProfileOption.PLAYERCOUNT_MAXPLAYERS_AMOUNT, "Max Players Count", entry.maxPlayersCount(), 100)
.appendBoolean(ProfileOption.PLAYERCOUNT_ONLINEPLAYERS_ENABLED, "Enable Online Players", entry.onlinePlayersEnabled())
.appendTextInput(ProfileOption.PLAYERCOUNT_ONLINEPLAYERS_AMOUNT, "Online Players Count", entry.onlinePlayersCount(), 100);
return builder.build();
}
public enum ProfileOption{
PRIORITY,
CONDITION,
MOTD,
FAVICON,
PLAYERCOUNT_HIDEPLAYERS,
PLAYERCOUNT_HIDEPLAYERSHOVER,
PLAYERCOUNT_HOVER,
PLAYERCOUNT_TEXT,
PLAYERCOUNT_EXTRAPLAYERS_ENABLED,
PLAYERCOUNT_EXTRAPLAYERS_AMOUNT,
PLAYERCOUNT_MAXPLAYERS_ENABLED,
PLAYERCOUNT_MAXPLAYERS_AMOUNT,
PLAYERCOUNT_ONLINEPLAYERS_ENABLED,
PLAYERCOUNT_ONLINEPLAYERS_AMOUNT;
public static List<String> list(){
return Arrays.stream(values())
.map(ProfileOption::toString)
.toList();
}
public static ProfileOption fromString(String input){
for(ProfileOption option : values()){
if(option.toString().equalsIgnoreCase(input))
return option;
}
return null;
}
@Override
public String toString(){
return this.name().toLowerCase(Locale.ROOT).replace("_", ".");
}
public String toDialogKey(){
return this.name().toLowerCase(Locale.ROOT);
}
}
}
@@ -0,0 +1,44 @@
package ch.andre601.advancedserverlist.core.commands;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
import org.incendo.cloud.annotations.Command;
import org.incendo.cloud.annotations.CommandDescription;
import org.incendo.cloud.annotations.Permission;
@Command("advancedserverlist|asl")
@CommandDescription("Main Command of the plugin")
@Permission("advancedserverlist.admin")
@CommandType(CommandType.Type.ALL)
public record CmdReload(AdvancedServerList<?> core){
public static CmdReload create(AdvancedServerList<?> core){
return new CmdReload(core);
}
@Command("reload")
@CommandDescription("Reloads the plugin's main config and its profiles.")
@Permission({"advancedserverlist.admin", "advancedserverlist.command.reload"})
@CommandType(CommandType.Type.ALL)
public void handle(CmdSender sender){
sender.sendPrefixedMsg("Reloading plugin...");
if(core().fileHandler().reloadConfig()){
sender.sendPrefixedMsg("<green>Reloaded </green>config.yml<green>!");
}else{
sender.sendErrorMsg("<red>Encountered an error while reloading the </red>config.yml<red>.");
sender.sendErrorMsg("<red>See the server console for details.");
}
if(core().fileHandler().reloadProfiles()){
sender.sendPrefixedMsg("<green>(Re)loaded <white>%d</white> Profile(s)!", core().fileHandler().profiles().size());
}else{
sender.sendErrorMsg("<red>Encountered errors while (re)loading profiles.");
sender.sendErrorMsg("<red>See the server console for details.");
}
sender.sendPrefixedMsg("<green>Reload complete!");
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022-2023 Andre_601
* Copyright (c) 2022-2025 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,456 +20,329 @@
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.core.commands;
import ch.andre601.advancedserverlist.api.objects.NullBool;
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
import ch.andre601.advancedserverlist.core.interfaces.commands.PluginCommand;
import ch.andre601.advancedserverlist.core.migration.minimotd.MiniMOTDConfigMigrator;
import ch.andre601.advancedserverlist.core.migration.serverlistplus.SLPConfigMigrator;
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
import ch.andre601.advancedserverlist.core.profiles.profile.ProfileSerializer;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.serialize.SerializationException;
import org.spongepowered.configurate.yaml.NodeStyle;
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.key.CloudKey;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.*;
public class CommandHandler{
private final List<PluginCommand> subCommands;
public static final CloudKey<CommandType.Type> COMMAND_TYPE = CloudKey.of("command_type", CommandType.Type.class);
public static final CloudKey<String> OPTION = CloudKey.of("profile_option", String.class);
public CommandHandler(AdvancedServerList<?> core){
subCommands = List.of(
new Help(core),
new Reload(core),
new ClearCache(core),
new Migrate(core),
new Profiles(core)
);
}
public List<PluginCommand> getSubCommands(){
return subCommands;
}
public void handle(CmdSender sender, String[] args){
if(args.length == 0){
sender.sendErrorMsg("<red>Too few arguments! Use <grey>/asl help</grey> for a list of commands.");
public static void handleSet(AdvancedServerList<?> core, CommandContext<CmdSender> context){
CmdSender sender = context.sender();
String profile = context.get("profile");
String value = getOptionValue(context.getOrDefault("value", null));
String option = context.command().commandMeta().getOrDefault(OPTION, "");
ServerListProfile slp = profile(core, profile);
if(slp == null){
sender.sendErrorMsg("<red>No profile with name</red> %s <red>found!", profile);
return;
}
for(PluginCommand subCommand : subCommands){
if(subCommand.name().equalsIgnoreCase(args[0])){
if(!sender.hasPermission(subCommand.permission())){
sender.sendErrorMsg(
"<red>You do not have the permission <grey>%s</grey> to execute this command.",
subCommand.permission()
);
return;
int priority = slp.priority();
String condition = slp.condition();
ProfileEntry entry = slp.defaultProfile();
ProfileEntry.Builder builder = slp.defaultProfile().builder();
List<String> oldValues = new ArrayList<>();
List<String> newValues = new ArrayList<>();
switch(CmdProfiles.ProfileOption.fromString(option)){
case PRIORITY -> {
oldValues.add(String.valueOf(slp.priority()));
if(value == null || value.isEmpty()){
priority = 0;
break;
}
subCommand.handle(sender, Arrays.copyOfRange(args, 1, args.length));
try{
priority = Integer.parseInt(value);
newValues.add(String.valueOf(priority));
}catch(NumberFormatException ex){
sender.sendErrorMsg("<red>Invalid value provided. Expected number but got</red> %s", value);
return;
}
}
case CONDITION -> {
oldValues.add(slp.condition());
if(value == null || value.isEmpty()){
condition = "";
break;
}
condition = value;
newValues.add(value);
}
case MOTD -> {
oldValues.addAll(entry.motd());
if(value == null || value.isEmpty()){
builder.motd(Collections.emptyList());
break;
}
String[] lines = value.split("\\\\n|<newline>|<br>");
if(lines.length == 0){
builder.motd(Collections.emptyList());
break;
}
if(lines.length > 1){
if(lines.length > 2)
sender.sendPrefixedMsg("<gold>Found %d Lines for MOTD. Only using first 2 lines...", lines.length);
builder.motd(Arrays.asList(lines));
}else{
builder.motd(Collections.singletonList(lines[0]));
}
newValues.addAll(Arrays.asList(lines));
}
case FAVICON -> {
oldValues.add(entry.favicon());
if(value == null || value.isEmpty()){
builder.favicon("");
break;
}
builder.favicon(value);
newValues.add(value);
}
case PLAYERCOUNT_HIDEPLAYERS -> {
if(!entry.hidePlayersEnabled().isNotSet())
oldValues.add(String.valueOf(entry.hidePlayersEnabled().getOrDefault(false)));
if(value == null || value.isEmpty()){
builder.hidePlayersEnabled(NullBool.NOT_SET);
break;
}
NullBool bool = NullBool.resolve(Boolean.parseBoolean(value));
builder.hidePlayersEnabled(bool);
newValues.add(String.valueOf(bool.getOrDefault(false)));
}
case PLAYERCOUNT_HIDEPLAYERSHOVER -> {
if(!entry.hidePlayersHoverEnabled().isNotSet())
oldValues.add(String.valueOf(entry.hidePlayersHoverEnabled().getOrDefault(false)));
if(value == null || value.isEmpty()){
builder.hidePlayersHoverEnabled(NullBool.NOT_SET);
break;
}
NullBool bool = NullBool.resolve(Boolean.parseBoolean(value));
builder.hidePlayersHoverEnabled(bool);
newValues.add(String.valueOf(bool.getOrDefault(false)));
}
case PLAYERCOUNT_HOVER -> {
oldValues.addAll(entry.players());
if(value == null || value.isEmpty()){
builder.players(Collections.emptyList());
break;
}
String[] lines = value.split("\\\\n|<newline>");
if(lines.length == 0){
builder.players(Collections.emptyList());
break;
}
builder.players(Arrays.asList(lines));
newValues.addAll(Arrays.asList(lines));
}
case PLAYERCOUNT_TEXT -> {
if(!entry.playerCountText().isEmpty())
oldValues.add(entry.playerCountText());
if(value == null || value.isEmpty()){
builder.playerCountText("");
break;
}
builder.playerCountText(value);
newValues.add(value);
}
case PLAYERCOUNT_EXTRAPLAYERS_ENABLED -> {
if(!entry.extraPlayersEnabled().isNotSet())
oldValues.add(String.valueOf(entry.extraPlayersEnabled().getOrDefault(false)));
if(value == null || value.isEmpty()){
builder.extraPlayersEnabled(NullBool.NOT_SET);
break;
}
NullBool bool = NullBool.resolve(Boolean.parseBoolean(value));
builder.extraPlayersEnabled(bool);
newValues.add(String.valueOf(bool.getOrDefault(false)));
}
case PLAYERCOUNT_EXTRAPLAYERS_AMOUNT -> {
if(!entry.extraPlayersCount().isEmpty())
oldValues.add(entry.extraPlayersCount());
if(value == null || value.isEmpty()){
builder.extraPlayersCount("");
break;
}
builder.extraPlayersCount(value);
newValues.add(value);
}
case PLAYERCOUNT_MAXPLAYERS_ENABLED -> {
if(!entry.maxPlayersEnabled().isNotSet())
oldValues.add(String.valueOf(entry.maxPlayersEnabled().getOrDefault(false)));
if(value == null || value.isEmpty()){
builder.maxPlayersEnabled(NullBool.NOT_SET);
break;
}
NullBool bool = NullBool.resolve(Boolean.parseBoolean(value));
builder.maxPlayersEnabled(bool);
newValues.add(String.valueOf(bool.getOrDefault(false)));
}
case PLAYERCOUNT_MAXPLAYERS_AMOUNT -> {
if(!entry.maxPlayersCount().isEmpty())
oldValues.add(entry.extraPlayersCount());
if(value == null || value.isEmpty()){
builder.maxPlayersCount("");
break;
}
builder.maxPlayersCount(value);
newValues.add(value);
}
case PLAYERCOUNT_ONLINEPLAYERS_ENABLED -> {
if(!entry.onlinePlayersEnabled().isNotSet())
oldValues.add(String.valueOf(entry.onlinePlayersEnabled().getOrDefault(false)));
if(value == null || value.isEmpty()){
builder.onlinePlayersEnabled(NullBool.NOT_SET);
break;
}
NullBool bool = NullBool.resolve(Boolean.parseBoolean(value));
builder.onlinePlayersEnabled(bool);
newValues.add(String.valueOf(bool.getOrDefault(false)));
}
case PLAYERCOUNT_ONLINEPLAYERS_AMOUNT -> {
if(!entry.onlinePlayersCount().isEmpty())
oldValues.add(entry.onlinePlayersCount());
if(value == null || value.isEmpty()){
builder.onlinePlayersCount("");
break;
}
builder.onlinePlayersCount(value);
newValues.add(value);
}
case null, default -> {
sender.sendErrorMsg("<red>Unknown option </red>%s<red>!", option);
return;
}
}
sender.sendErrorMsg("<red>Unknown subcommand <grey>%s</grey>. Use <grey>/asl help</grey> for a list of commands.", args[0]);
}
private static class Help extends PluginCommand{
private final AdvancedServerList<?> core;
public Help(AdvancedServerList<?> core){
super("help");
this.core = core;
}
@Override
public void handle(CmdSender sender, String[] args){
sender.sendPrefixedMsg("- Commands. Hover for details.");
for(PluginCommand command : core.getCommandHandler().getSubCommands()){
sender.sendMsg();
sender.sendMsg(
"<click:suggest_command:/asl %s>" +
command.usage() +
"</click>",
command.name()
if(core.fileHandler().saveProfile(profileName(profile), priority, condition, slp.profiles(), builder.build())){
if(sender.isPlayer()){
sender.sendPrefixedMsg(
"<green>Successfully updated <white>[<grey><hover:show_text:\"%s\">%s</hover></grey>]</white>!",
changeValueHover(oldValues, newValues),
option
);
sender.sendMsg("<grey>[<hover:show_text:\"%s\"><white>Permission</white></hover>]</grey>", command.permission());
}
}
@Override
public String usage(){
return new CommandStringBuilder("help", description())
.build();
}
@Override
public String description(){
return "Shows all available commands.";
}
}
private static class Reload extends PluginCommand{
private final AdvancedServerList<?> core;
public Reload(AdvancedServerList<?> core){
super("reload");
this.core = core;
}
@Override
public void handle(CmdSender sender, String[] args){
sender.sendPrefixedMsg("Reloading plugin...");
if(core.getFileHandler().reloadConfig()){
sender.sendPrefixedMsg("<green>Reloaded <grey>config.yml</grey>!");
}else{
sender.sendErrorMsg("<red>Error while reloading <grey>config.yml</grey>!");
}
if(core.getFileHandler().reloadProfiles()){
sender.sendPrefixedMsg("<green>Loaded <grey>%d profile(s)</grey>!", core.getFileHandler().getProfiles().size());
}else{
sender.sendErrorMsg("<red>Error while loading profile(s)!");
}
sender.sendPrefixedMsg("<green>Reload complete!");
}
@Override
public String usage(){
return new CommandStringBuilder("reload", description())
.build();
}
@Override
public String description(){
return "Reloads the main config and all profiles.";
}
}
private static class ClearCache extends PluginCommand{
private final AdvancedServerList<?> core;
public ClearCache(AdvancedServerList<?> core){
super("clearcache");
this.core = core;
}
@Override
public void handle(CmdSender sender, String[] args){
sender.sendPrefixedMsg("Clearing caches...");
core.clearFaviconCache();
sender.sendPrefixedMsg("<green>Successfully cleared Favicon Cache!");
core.clearPlayerCache();
sender.sendPrefixedMsg("<green>Successfully cleared Player Cache!");
sender.sendPrefixedMsg("<green>Cache clearing complete!");
}
@Override
public String usage(){
return new CommandStringBuilder("clearcache", description())
.build();
}
@Override
public String description(){
return "Clears the Favicon and Player Cache.";
}
}
private static class Migrate extends PluginCommand{
private final AdvancedServerList<?> core;
public Migrate(AdvancedServerList<?> core){
super("migrate");
this.core = core;
}
@Override
public void handle(CmdSender sender, String[] args){
if(args.length == 0){
sender.sendErrorMsg("<red>Insufficient arguments! Please provide a plugin to migrate from.");
sender.sendErrorMsg("<red>Available options:");
sender.sendErrorMsg(" - ServerListPlus");
sender.sendErrorMsg(" - MiniMOTD");
return;
}
if(args[0].equalsIgnoreCase("serverlistplus")){
if(!core.getPlugin().isPluginEnabled("ServerListPlus")){
sender.sendErrorMsg("<red>Plugin ServerListPlus is not enabled.");
sender.sendErrorMsg("<red>It needs to be active for the migration to work!");
return;
}
sender.sendPrefixedMsg("<green>Successfully updated <white>[<grey>%s</grey>]</white>:", option);
sender.sendPrefixedMsg("Migrating ServerListPlus configuration file...");
int migrated = SLPConfigMigrator.migrate(core, sender);
if(migrated == 0){
sender.sendErrorMsg("<red>Unable to migrate any ServerListPlus configuration. Check console for details!");
if(oldValues.isEmpty()){
sender.sendMsg("<red>-</red> <white><i>Not set</i></white>");
}else{
sender.sendPrefixedMsg("<green>Migration complete!");
oldValues.forEach(oldValue -> sender.sendMsg("<red>-</red> %s", oldValue));
}
}else
if(args[0].equalsIgnoreCase("minimotd")){
sender.sendPrefixedMsg("Migrating MiniMOTD configuration file...");
if(MiniMOTDConfigMigrator.migrate(core, sender)){
sender.sendPrefixedMsg("<green>Migration complete!");
sender.sendMsg();
if(newValues.isEmpty()){
sender.sendMsg("<green>+</green> <white><i>Reset to default</i></white>");
}else{
sender.sendErrorMsg("<red>Unable to migrate MiniMOTD configuration. Check console for details!");
newValues.forEach(newValue -> sender.sendMsg("<green>+</green> %s", newValue));
}
}else{
sender.sendErrorMsg("<red>Unknown plugin <grey>%s</grey>. Available Options:", args[0]);
sender.sendErrorMsg(" - ServerListPlus");
sender.sendErrorMsg(" - MiniMOTD");
}
}
@Override
public String usage(){
return new CommandStringBuilder("migrate", description())
.appendSpace()
.appendArgument(new CommandStringBuilder.ArgumentBuilder("Plugin").description(
"""
The Plugin to migrate from.
Supported options:
- <white>ServerListPlus</white>
- <white>MiniMOTD</white>
"""
).build())
.build();
}
@Override
public String description(){
return "Migrates the configuration from supported Plugins to AdvancedServerList.";
}
}
private static class Profiles extends PluginCommand{
private final AdvancedServerList<?> core;
public Profiles(AdvancedServerList<?> core){
super("profiles");
this.core = core;
}
@Override
public void handle(CmdSender sender, String[] args){
if(args.length == 0){
sender.sendErrorMsg("<red>Invalid command usage.");
sender.sendErrorMsg("<red>Usage:</red> %s", usage());
return;
}
switch(args[0].toLowerCase(Locale.ROOT)){
case "list" -> {
sender.sendPrefixedMsg("Available Profiles:");
core.getFileHandler().getProfiles().stream()
.sorted(Comparator.comparingInt(ServerListProfile::priority).reversed())
.forEach(
profile -> sender.sendPrefixedMsg("- <white><hover:show_text:\"%s\">%s</hover></white>", getHover(profile), profile.file())
);
}
case "add" -> {
if(args.length == 1){
sender.sendErrorMsg("<red>Insufficient arguments.");
sender.sendErrorMsg("<red>Usage:</red> /asl profiles add <name>");
return;
}
String name = getProfileName(args[1]);
if(core.getFileHandler().getProfiles().stream().anyMatch(profile -> profile.file().equalsIgnoreCase(name))){
sender.sendErrorMsg("<red>A profile with file name</red> %s <red>already exists!", name);
return;
}
if(core.getFileHandler().createFile(name)){
sender.sendPrefixedMsg("<green>Successfully created</green> %s<green>!", name);
sender.sendPrefixedMsg("<green>Load the file using</green> /asl reload");
}else{
sender.sendErrorMsg("<red>Error while trying to create</red> %s<red>!", name);
sender.sendErrorMsg("<red>Check console for further details.");
}
}
case "copy" -> {
if(args.length < 3){
sender.sendErrorMsg("<red>Insufficient arguments.");
sender.sendErrorMsg("<red>Usage:</red> /asl profiles copy <profile> <name>");
return;
}
String name = getProfileName(args[1]);
String copy = getProfileName(args[2]);
ServerListProfile targetProfile = core.getFileHandler().getProfiles().stream()
.filter(file -> file.file().equalsIgnoreCase(name))
.findFirst()
.orElse(null);
boolean copyExists = core.getFileHandler().getProfiles().stream()
.anyMatch(profile -> profile.file().equals(copy));
if(targetProfile == null){
sender.sendErrorMsg("<red>There is no file with name</red> %s<red>!", name);
return;
}
if(copyExists){
sender.sendErrorMsg("<red>A profile with file name</red> %s <red>already exists!", copy);
return;
}
Path profilePath = core.getPlugin().getFolderPath().resolve("profiles").resolve(copy);
try{
Files.createFile(profilePath);
}catch(IOException ex){
sender.sendErrorMsg("<red>There was an error while creating the file</red> %s<red>!", copy);
sender.sendErrorMsg("<red>Check console for further details.");
core.getPlugin().getPluginLogger().warn("Encountered IOException while creating file <white>%s</white>", ex, copy);
return;
}
YamlConfigurationLoader loader = YamlConfigurationLoader.builder()
.path(profilePath)
.indent(2)
.nodeStyle(NodeStyle.BLOCK)
.defaultOptions(options -> options.serializers(builder -> builder.register(ProfileEntry.class, ProfileSerializer.INSTANCE)))
.build();
ConfigurationNode node;
try{
node = loader.load();
}catch(IOException ex){
sender.sendErrorMsg("<red>There was an error while loading the file</red> %s<red>!", copy);
sender.sendErrorMsg("<red>Check console for further details.");
core.getPlugin().getPluginLogger().warn("Encountered IOException while loading file <white>%s</white>", ex, copy);
return;
}
if(node == null){
sender.sendErrorMsg("Cannot copy</red> %s <red>to</red> %s<red>. ConfigurationNode was null.", name, copy);
return;
}
try{
node.node("priority").set(targetProfile.priority());
node.node("condition").set(targetProfile.condition());
node.node("profiles").setList(ProfileEntry.class, targetProfile.profiles());
node.set(ProfileEntry.class, targetProfile.defaultProfile());
}catch(SerializationException ex){
sender.sendErrorMsg("<red>There was an Error while copying values from</red> %s <red>to</red> %s<red>.", name, copy);
sender.sendErrorMsg("<red>Check console for details.");
core.getPlugin().getPluginLogger().warn("Encountered SerializationException while set values for <white>%s</white>!", ex, copy);
return;
}
try{
loader.save(node);
sender.sendPrefixedMsg("<green>Successfully created copy of</green> %s <green>as</green> %s<green>!", name, copy);
sender.sendPrefixedMsg("<green>Load the file using</green> /asl reload");
}catch(IOException ex){
sender.sendErrorMsg("<red>There was an Error while trying to save</red> %s<red>!", copy);
sender.sendErrorMsg("<red>Check console for details.");
core.getPlugin().getPluginLogger().warn("Encountered IOException while saving ConfigurationNode for <white>%s</white>!", ex, copy);
}
}
default -> {
sender.sendErrorMsg("<red>Unknown argument</red> %s<red>!", args[0]);
sender.sendErrorMsg("<red>Usage:</red> %s", usage());
}
}
}
@Override
public String usage(){
return new CommandStringBuilder("profiles", "Creates a new profile, copies one from an existing file, or lists all available profiles.")
.appendSpace()
.appendText("<grey>\\<")
// add <name>
.appendLiteralArgument("add", "Adds a new profile using the default values.")
.appendSpace()
.appendArgument(new CommandStringBuilder.ArgumentBuilder("name").description("Name of the new profile").required().build())
// Divider
.appendSpace()
.appendText("|")
.appendSpace()
// copy <profile> <name>
.appendLiteralArgument("copy", "Creates a copy of the provided profile.")
.appendSpace()
.appendArgument(new CommandStringBuilder.ArgumentBuilder("profile").description("The profile to copy.").required().build())
.appendSpace()
.appendArgument(new CommandStringBuilder.ArgumentBuilder("name").description("Name of the copy.").required().build())
// Divider
.appendSpace()
.appendText("|")
.appendSpace()
// list
.appendLiteralArgument("list", "Lists all currently know profiles and wether they are valid or not.")
// Closing bracket
.appendText("></grey>")
.build();
}
@Override
public String description(){
return "Creates a new profile, copies one from an existing file, or lists all available profiles.";
}
private String getHover(ServerListProfile profile){
return String.format(
"""
<grey>Priority: <white>%s</white>
Condition: <white>%s</white>
Valid profile: %s
""",
profile.priority(),
profile.condition() == null || profile.condition().isEmpty() ? "<i>None</i>" : profile.condition(),
profile.isInvalidProfile() ? "<red>No</red>" : "<green>Yes</green>"
sender.sendMsg();
}
sender.sendPrefixedMsg(
"<green>Use <white><click:suggest_command:/asl reload>/asl reload</click></white> to apply changes."
);
}
private String getProfileName(String name){
String fileName = name.toLowerCase(Locale.ROOT);
}else{
sender.sendErrorMsg("<red>Encountered an IOException while trying to save file</red> %s<red>.", profile);
sender.sendErrorMsg("<red>Check console for details.");
return fileName.endsWith(".yml") ? fileName : fileName + ".yml";
core.plugin().pluginLogger().warn("Encountered IOException while saving file <white>%s.yml</white>", profile);
}
}
public static ServerListProfile profile(AdvancedServerList<?> core, String name){
return core.fileHandler().profiles().stream()
.filter(p -> p.file().equalsIgnoreCase(profileName(name)))
.findFirst()
.orElse(null);
}
public static String profileName(String name){
if(name == null || name.isEmpty())
return "";
String filename = name.toLowerCase(Locale.ROOT);
return filename.endsWith(".yml") ? filename : filename + ".yml";
}
private static String changeValueHover(List<String> oldValues, List<String> newValues){
return """
<red>-</red> %s<reset>
<green>+</green> %s""".formatted(
oldValues.isEmpty() ? "<i>Not set</i>" : String.join("<reset>\n<red>-</red> ", oldValues),
newValues.isEmpty() ? "<i>Not set</i>" : String.join("<reset>\n<green>+</green> ", newValues)
);
}
private static String getOptionValue(Object obj){
return switch(obj){
case null -> "";
case Boolean bool -> Boolean.toString(bool);
case Integer integer -> Integer.toString(integer);
case String str -> str;
default -> obj.toString();
};
}
}
@@ -1,110 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2022-2025 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package ch.andre601.advancedserverlist.core.commands;
import java.util.ArrayList;
import java.util.List;
public class CommandStringBuilder{
private final List<CommandPart> commandParts = new ArrayList<>();
public CommandStringBuilder(String subcommand, String description){
appendText("<hover:show_text:\"<grey>" + description + "</grey>\"><white>" + subcommand + "</white></hover>");
}
public CommandStringBuilder appendArgument(Argument argument){
this.commandParts.add(argument);
return this;
}
public CommandStringBuilder appendText(String text){
this.commandParts.add(new Text(text));
return this;
}
public CommandStringBuilder appendLiteralArgument(String name, String description){
return appendText("<hover:show_text:\"<grey>" + description + "</grey>\"><white>" + name + "</white></hover>");
}
public CommandStringBuilder appendSpace(){
return appendText(" ");
}
public String build(){
StringBuilder builder = new StringBuilder("<aqua>/asl ");
for(CommandPart part : commandParts){
builder.append(part.build());
}
builder.append("</aqua>"); // Closing the first color tag.
return builder.toString();
}
public static class ArgumentBuilder{
private final String name;
private String description = "No description provided.";
private boolean required = false;
public ArgumentBuilder(String name){
this.name = name;
}
public ArgumentBuilder description(String description){
this.description = description;
return this;
}
public ArgumentBuilder required(){
this.required = true;
return this;
}
public Argument build(){
return new Argument(name, description, required);
}
}
private record Text(String text) implements CommandPart{
@Override
public String build(){
return text();
}
}
private record Argument(String name, String description, boolean required) implements CommandPart{
@Override
public String build(){
return "<hover:show_text:\"<grey>" + description() + "</grey>\">" +
"<grey>" + (required() ? "\\<" : "[") + "<white>" + name() + "</white>" + (required() ? ">" : "]") +
"</grey></hover>";
}
}
private interface CommandPart{
String build();
}
}
@@ -0,0 +1,22 @@
package ch.andre601.advancedserverlist.core.commands;
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
import ch.andre601.advancedserverlist.core.interfaces.commands.CommandType;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.incendo.cloud.execution.postprocessor.CommandPostprocessingContext;
import org.incendo.cloud.execution.postprocessor.CommandPostprocessor;
import org.incendo.cloud.services.type.ConsumerService;
public class CommandTypePostprocessor implements CommandPostprocessor<CmdSender>{
@Override
public void accept(@NonNull CommandPostprocessingContext<CmdSender> context){
CmdSender sender = context.commandContext().sender();
CommandType.Type type = context.commandContext().command().commandMeta().get(CommandHandler.COMMAND_TYPE);
if(type == CommandType.Type.ALL || (type == CommandType.Type.PLAYER_ONLY && sender.isPlayer()))
return;
sender.sendErrorMsg("<red>This command can only be executed by players!");
ConsumerService.interrupt();
}
}
@@ -0,0 +1,172 @@
/*
* MIT License
*
* Copyright (c) 2022-2025 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package ch.andre601.advancedserverlist.core.commands;
import ch.andre601.advancedserverlist.api.objects.NullBool;
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class ProfileOptionsSender{
private final List<OptionStringBuilder> builders = new ArrayList<>();
private final String file;
public ProfileOptionsSender(String file){
this.file = file;
}
public ProfileOptionsSender append(OptionStringBuilder builder){
this.builders.add(builder.file(file));
return this;
}
public void send(CmdSender sender){
sender.sendMsg();
sender.sendPrefixedMsg("Profile Info [<white>%s</white>]", file);
for(OptionStringBuilder builder : builders){
sender.sendMsg(builder.build());
}
sender.sendMsg();
sender.sendMsg("[<grey>Hover for info, click for command</grey>]");
sender.sendMsg();
}
public static class OptionStringBuilder{
public static OptionStringBuilder of(String displayName, String hover){
return new OptionStringBuilder(displayName)
.hover(hover == null || hover.isEmpty() ? "<i>Not set</i>" : hover)
.color(hover == null || hover.isEmpty() ? ColorName.RED : ColorName.GREEN);
}
public static OptionStringBuilder of(String displayName, List<String> hover){
return new OptionStringBuilder(displayName)
.hover(hover == null || hover.isEmpty() ? "<i>Not set</i>" : String.join("\n<reset>", hover))
.color(hover == null || hover.isEmpty() ? ColorName.RED : ColorName.GREEN);
}
public static OptionStringBuilder of(String displayName, NullBool hover){
return new OptionStringBuilder(displayName)
.hover(hover == null || !hover.getOrDefault(false) ? "<red>Disabled</red>" : "<green>Enabled</green>")
.color(hover == null || !hover.getOrDefault(false) ? ColorName.RED : ColorName.GREEN);
}
public static OptionStringBuilder of(String displayName, int hover){
return new OptionStringBuilder(displayName)
.hover(String.valueOf(hover));
}
public static OptionStringBuilder of(String displayName){
return new OptionStringBuilder(displayName).hover(null);
}
private final String displayName;
private String file = null;
private String prefix = "├─";
private ColorName color = ColorName.AQUA;
private String hover = "<i>Not set</i>";
private CmdProfiles.ProfileOption option = null;
private OptionStringBuilder(String displayName){
this.displayName = displayName;
}
public OptionStringBuilder file(String file){
this.file = file;
return this;
}
public OptionStringBuilder prefix(String prefix){
this.prefix = prefix;
return this;
}
public OptionStringBuilder color(ColorName color){
this.color = color;
return this;
}
public OptionStringBuilder hover(String hover){
this.hover = hover;
return this;
}
public OptionStringBuilder option(CmdProfiles.ProfileOption option){
this.option = option;
return this;
}
public String build(){
StringBuilder builder = new StringBuilder("<grey>")
.append(prefix)
.append(" <white>[")
.append('<')
.append(color)
.append('>');
if(hover != null && !hover.isEmpty()){
builder.append("<hover:show_text:\"")
.append(hover)
.append("\">");
}
if(option != null && file != null && !file.isEmpty()){
builder.append("<click:suggest_command:/asl profiles set ")
.append(file)
.append(" ")
.append(option)
.append(" >");
}
builder.append(displayName);
if(option != null && file != null && !file.isEmpty())
builder.append("</click>");
if(hover != null && !hover.isEmpty())
builder.append("</hover>");
builder.append("</")
.append(color)
.append(">]</white></grey>");
return builder.toString();
}
}
public enum ColorName{
RED,
GREEN,
AQUA;
@Override
public String toString(){
return this.name().toLowerCase(Locale.ROOT);
}
}
}
@@ -4,7 +4,10 @@ import ch.andre601.advancedserverlist.api.PlaceholderProvider;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public class MaintenancePlaceholder extends PlaceholderProvider{
private final AdvancedServerList<?> plugin;
@@ -15,8 +18,8 @@ public class MaintenancePlaceholder extends PlaceholderProvider{
this.plugin = plugin;
}
@Override
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
if(!placeholder.equals("maintenanceEnabled") || !plugin.getPlugin().isPluginEnabled("Maintenance"))
public @Nullable String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
if(!placeholder.equals("maintenanceEnabled") || !plugin.plugin().isPluginEnabled("Maintenance"))
return null;
return String.valueOf(MaintenanceUtil.get().isMaintenanceEnabled());
@@ -0,0 +1,11 @@
package ch.andre601.advancedserverlist.core.compat.miniplaceholders;
import io.github.miniplaceholders.api.MiniPlaceholders;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
public class MiniPlaceholdersUtil{
public static TagResolver tagResolvers(){
return MiniPlaceholders.globalPlaceholders();
}
}
@@ -40,7 +40,7 @@ public class PAPIUtil{
private static PAPIUtil instance;
private final ValueCache<CompletableFuture<Set<String>>> serversCache = new ValueCache<>(Duration.ofSeconds(10));
private final Cache<UUID, CompletableFuture<String>> textCache = CacheBuilder.newBuilder()
private final Cache<CacheKey, CompletableFuture<String>> textCache = CacheBuilder.newBuilder()
.expireAfterWrite(Duration.ofSeconds(10))
.build();
private final PlaceholderAPI papi;
@@ -72,21 +72,23 @@ public class PAPIUtil{
if(values.isEmpty())
return null;
return List.copyOf(values).get(0);
return List.copyOf(values).getFirst();
}
public <P> P getPlayer(Collection<P> players){
if(players.isEmpty())
return null;
return List.copyOf(players).get(0);
return List.copyOf(players).getFirst();
}
public String parse(String text, UUID carrier, UUID player){
try{
return textCache.get(player, () -> papi.formatPlaceholders(text, carrier, player)).getNow(text);
return textCache.get(new CacheKey(player, text), () -> papi.formatPlaceholders(text, carrier, player)).getNow(text);
}catch(ExecutionException ex){
return text;
}
}
private record CacheKey(UUID uuid, String text){}
}
@@ -44,28 +44,28 @@ import java.util.List;
public class PingEventHandler{
public static <F, P extends GenericPlayer> ProfileEntry handleEvent(GenericEventWrapper<F, P> event){
event.getPlugin().getPluginLogger().debug(PingEventHandler.class, "Received ping event. Handling it...");
event.plugin().pluginLogger().debug(PingEventHandler.class, "Received ping event. Handling it...");
if(event.isInvalidProtocol()){
event.getPlugin().getPluginLogger().debug(PingEventHandler.class, "Not handling event. Protocol was invalid.");
event.plugin().pluginLogger().debug(PingEventHandler.class, "Not handling event. Protocol was invalid.");
return null;
}
PluginCore<F> plugin = event.getPlugin();
String host = event.getVirtualHost();
PluginLogger logger = plugin.getPluginLogger();
PluginCore<F> plugin = event.plugin();
String host = event.virtualHost();
PluginLogger logger = plugin.pluginLogger();
logger.debug(PingEventHandler.class, "Protocol valid. Continue handling...");
int online = event.getOnlinePlayers();
int max = event.getMaxPlayers();
int online = event.onlinePlayers();
int max = event.maxPlayers();
P player = event.createPlayer(
plugin.getCore().getPlayerHandler().getCachedPlayer(event.getPlayerIP()),
event.getProtocolVersion()
plugin.core().playerHandler().getCachedPlayer(event.playerIP()),
event.protocolVersion()
);
GenericServer server = event.createGenericServer(online, max, host);
GenericServer server = event.createServer(online, max, host);
ServerListProfile profile = ProfileManager.resolveProfile(plugin.getCore(), player, server);
ServerListProfile profile = ProfileManager.resolveProfile(plugin.core(), player, server);
if(profile == null){
logger.debugWarn(PingEventHandler.class, "Server List Profile couldn't be resolved properly. Cancelling event handling...");
@@ -93,11 +93,12 @@ public class PingEventHandler{
String onlinePlayers = ComponentParser.text(entry.onlinePlayersCount())
.modifyText(text -> StringReplacer.replace(text, player, server))
.modifyText(text -> event.parsePAPIPlaceholders(text, player))
.applyMMTags(event.plugin())
.toString();
try{
online = onlinePlayers == null ? online : Integer.parseInt(onlinePlayers);
event.setOnlinePlayers(online);
event.onlinePlayers(online);
}catch(NumberFormatException ex){
logger.warn("Option 'playerCount -> onlinePlayers -> amount' is not a valid Number!");
}
@@ -110,13 +111,14 @@ public class PingEventHandler{
String extraPlayers = ComponentParser.text(entry.extraPlayersCount())
.modifyText(text -> StringReplacer.replace(text, player, server))
.modifyText(text -> event.parsePAPIPlaceholders(text, player))
.applyMMTags(event.plugin())
.toString();
try{
max = online + (extraPlayers == null ? 0 : Integer.parseInt(extraPlayers));
logger.debug(PingEventHandler.class, "'playerCount -> extraPlayers -> amount' is a valid number. Applying it...");
event.setMaxPlayers(max);
event.maxPlayers(max);
}catch(NumberFormatException ex){
logger.warn("Option 'playerCount -> extraPlayers -> amount' is not a valid Number!");
}
@@ -127,19 +129,20 @@ public class PingEventHandler{
String maxPlayers = ComponentParser.text(entry.maxPlayersCount())
.modifyText(text -> StringReplacer.replace(text, player, server))
.modifyText(text -> event.parsePAPIPlaceholders(text, player))
.applyMMTags(event.plugin())
.toString();
try{
max = maxPlayers == null ? 0 : Integer.parseInt(maxPlayers);
logger.debug(PingEventHandler.class, "'playerCount -> maxPlayers -> amount' is a valid number. Applying it...");
event.setMaxPlayers(max);
event.maxPlayers(max);
}catch(NumberFormatException ex){
logger.warn("Option 'playerCount -> maxPlayers -> amount' is not a valid Number!");
}
}
GenericServer finalServer = event.createGenericServer(online, max, host);
GenericServer finalServer = event.createServer(online, max, host);
if(ProfileManager.checkOption(entry.motd()) && ignoreMaintenance(event, "motd")){
logger.debug(PingEventHandler.class, "'motd' option present. Applying ['%s']...", String.join("', '", entry.motd()));
@@ -148,12 +151,13 @@ public class PingEventHandler{
.map(line -> ComponentParser.text(line)
.modifyText(text -> StringReplacer.replace(text, player, finalServer))
.modifyText(text -> event.parsePAPIPlaceholders(text, player))
.modifyText(text -> plugin.getCore().getTextCenterUtil().getCenteredText(text))
.modifyText(text -> plugin.core().textCenterUtil().getCenteredText(text))
.applyMMTags(event.plugin())
.toComponent()
)
.toList();
event.setMotd(Component.join(JoinConfiguration.separator(Component.newline()), motd));
event.motd(Component.join(JoinConfiguration.separator(Component.newline()), motd));
}
boolean hidePlayers = ProfileManager.checkOption(entry.hidePlayersEnabled());
@@ -167,10 +171,11 @@ public class PingEventHandler{
if(ProfileManager.checkOption(entry.playerCountText()) && !hidePlayers && ignoreMaintenance(event, "playerCountText")){
logger.debug(PingEventHandler.class, "'playerCount -> text' option set. Applying '%s'...", entry.playerCountText());
event.setPlayerCount(
event.playerCount(
ComponentParser.text(entry.playerCountText())
.modifyText(text -> StringReplacer.replace(text, player, finalServer))
.modifyText(text -> event.parsePAPIPlaceholders(text, player))
.applyMMTags(event.plugin())
.toString()
);
}
@@ -178,30 +183,33 @@ public class PingEventHandler{
boolean hidePlayersHover = ProfileManager.checkOption(entry.hidePlayersHoverEnabled());
if(hidePlayersHover){
logger.debug(PingEventHandler.class, "'playerCount -> hidePlayersHover' option set. Hiding Player Hover...");
event.setPlayersHidden();
event.playersHidden();
}
if(ProfileManager.checkOption(entry.players()) && !hidePlayers && !hidePlayersHover && ignoreMaintenance(event, "playerCountHover")){
logger.debug(PingEventHandler.class, "'playerCount -> hover' option set. Applying ['%s']...", String.join("', '", entry.players()));
event.setPlayers(entry.players(), player, server);
event.players(entry.players(), player, server);
}
if(ProfileManager.checkOption(entry.favicon()) && ignoreMaintenance(event, "favicon")){
logger.debug(PingEventHandler.class, "'favicon' option set. Resolving '%s'...", entry.favicon());
String faviconString = StringReplacer.replace(entry.favicon(), player, server);
String faviconString = StringReplacer.modifier(entry.favicon())
.modify(text -> StringReplacer.replace(text, player, server))
.modify(text -> event.parsePAPIPlaceholders(text, player))
.asString();
F favicon = plugin.getFaviconHandler().getFavicon(faviconString);
F favicon = plugin.faviconHandler().favicon(faviconString);
if(favicon == null){
logger.debugWarn(PingEventHandler.class, "Favicon was invalid or not yet resolved! Using default favicon of Server/Proxy...");
event.setDefaultFavicon();
event.defaultFavicon();
}else{
logger.debug(PingEventHandler.class, "Applying favicon...");
event.setFavicon(favicon);
event.favicon(favicon);
}
}
@@ -215,6 +223,6 @@ public class PingEventHandler{
if(!event.isMaintenanceModeActive())
return true;
return !event.getPlugin().getCore().getFileHandler().getBool(true, "disableDuringMaintenance", option);
return !event.plugin().core().fileHandler().getBool(true, "disableDuringMaintenance", option);
}
}
@@ -46,7 +46,9 @@ public class ConfigMigrator{
public static ConfigurationTransformation.Versioned create(){
return ConfigurationTransformation.versionedBuilder()
.versionKey("configVersion")
.addVersion(LATEST, sevenToEight())
.addVersion(LATEST, nineToTen())
.addVersion(9, eightToNine())
.addVersion(8, sevenToEight())
.addVersion(7, sixToSeven())
.addVersion(6, fiveToSix())
.addVersion(5, fourToFive())
@@ -56,6 +58,22 @@ public class ConfigMigrator{
.build();
}
public static ConfigurationTransformation nineToTen(){
return ConfigurationTransformation.builder()
.addAction(NodePath.path("faviconStrategy"), TransformAction.rename("faviconResizeStrategy"))
.build();
}
public static ConfigurationTransformation eightToNine(){
return ConfigurationTransformation.builder()
.addAction(NodePath.path(), (path, value) -> {
value.node("faviconStrategy").set("resize");
return null;
})
.build();
}
public static ConfigurationTransformation sevenToEight(){
return ConfigurationTransformation.builder()
.addAction(NodePath.path("disableDuringMaintenance"), (path, value) -> {
@@ -32,6 +32,7 @@ import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
import ch.andre601.advancedserverlist.core.profiles.profile.ProfileSerializer;
import org.spongepowered.configurate.ConfigurateException;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.serialize.SerializationException;
import org.spongepowered.configurate.yaml.NodeStyle;
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
@@ -46,6 +47,7 @@ import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
public class FileHandler{
@@ -61,18 +63,27 @@ public class FileHandler{
public FileHandler(AdvancedServerList<?> core){
this.plugin = core;
this.logger = core.getPlugin().getPluginLogger();
this.logger = core.plugin().pluginLogger();
this.config = core.getPlugin().getFolderPath().resolve("config.yml");
this.profilesFolder = core.getPlugin().getFolderPath().resolve("profiles");
this.config = core.plugin().folderPath().resolve("config.yml");
this.profilesFolder = core.plugin().folderPath().resolve("profiles");
}
public List<ServerListProfile> getProfiles(){
public List<ServerListProfile> profiles(){
return profiles;
}
public ServerListProfile profile(String file){
for(ServerListProfile profile : profiles){
if(profile.file().equalsIgnoreCase(file))
return profile;
}
return null;
}
public boolean loadConfig(){
File folder = plugin.getPlugin().getFolderPath().toFile();
File folder = plugin.plugin().folderPath().toFile();
if(!folder.exists() && !folder.mkdirs()){
logger.warn("Couldn't create folder for plugin. Is it missing Write permissions?");
return false;
@@ -106,6 +117,48 @@ public class FileHandler{
}
}
public boolean saveProfile(String file, int priority, String condition, List<ProfileEntry> profiles, ProfileEntry defaultProfile){
String filename = file.toLowerCase(Locale.ROOT);
Path filePath = profilesFolder.resolve(filename.endsWith(".yml") ? filename : filename + ".yml");
if(!Files.exists(filePath)){
logger.warn("Cannot find profile with file name '<white>%s</white>'", filename);
return false;
}
YamlConfigurationLoader loader = YamlConfigurationLoader.builder()
.defaultOptions(opts -> opts.serializers(build -> build.register(ProfileEntry.class, ProfileSerializer.INSTANCE)))
.path(filePath)
.nodeStyle(NodeStyle.BLOCK)
.build();
ConfigurationNode node;
try{
node = loader.load();
}catch(IOException ex){
logger.warn("Encountered an IOException while loading file '<white>%s</white>'", ex, filename);
return false;
}
if(node == null)
return false;
try{
node.node("priority").set(priority);
node.node("condition").set(condition);
node.node("profiles").setList(ProfileEntry.class, profiles);
node.set(ProfileEntry.class, defaultProfile);
loader.save(node);
return true;
}catch(SerializationException ex){
logger.warn("Encountered a SerializationException while saving file '<white>%s</white>'", ex, filename);
return false;
}catch(IOException ex){
logger.warn("Encountered an IOException while saving file '<white>%s</white>'", ex, filename);
return false;
}
}
public boolean createFile(String name){
if(Files.notExists(profilesFolder) && profilesFolder.toFile().mkdirs()){
logger.debugSuccess(FileHandler.class, "Successfully created profiles folder.");
@@ -219,7 +272,7 @@ public class FileHandler{
private boolean makeBackup(){
logger.info("[-] Making backup of old config.yml...");
File backups = plugin.getPlugin().getFolderPath().resolve("backups").toFile();
File backups = plugin.plugin().folderPath().resolve("backups").toFile();
if(!backups.exists() && !backups.mkdirs()){
logger.failure("Cannot create backups folder for migration!");
return false;
@@ -25,12 +25,12 @@
package ch.andre601.advancedserverlist.core.interfaces.commands;
import net.kyori.adventure.audience.Audience;
public interface CmdSender{
String prefix = "<grey>[<gradient:aqua:white>AdvancedServerList</gradient>] ";
String errorPrefix = "<grey>[<gradient:dark_red:red>AdvancedServerList</gradient>] ";
boolean hasPermission(String permission);
String prefix = "<grey>[<aqua>ASL</aqua>] ";
String errorPrefix = "<grey>[<red>ASL</red>] ";
void sendMsg(String msg, Object... args);
@@ -45,4 +45,8 @@ public interface CmdSender{
default void sendErrorMsg(String msg, Object... args){
sendMsg(errorPrefix + msg, args);
}
Audience audience();
boolean isPlayer();
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022-2023 Andre_601
* Copyright (c) 2022-2025 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,17 +20,15 @@
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.bungeecord.listeners;
package ch.andre601.advancedserverlist.core.interfaces.commands;
import ch.andre601.advancedserverlist.api.bungeecord.events.PreServerListSetEvent;
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
import org.incendo.cloud.CommandManager;
import org.incendo.cloud.SenderMapper;
public class PreServerListSetEventImpl extends PreServerListSetEvent{
public interface CommandManagerInterface<S>{
CommandManager<CmdSender> commandHandler();
public PreServerListSetEventImpl(ProfileEntry entry){
super(entry);
}
SenderMapper<S, CmdSender> senderMapper();
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022-2023 Andre_601
* Copyright (c) 2022-2025 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -20,32 +20,22 @@
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.core.interfaces.commands;
import java.util.Locale;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public abstract class PluginCommand{
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface CommandType{
Type value();
private final String name;
public PluginCommand(String name){
this.name = name;
enum Type{
ALL,
PLAYER_ONLY
}
public String name(){
return name;
}
public String permission(){
return "advancedserverlist.command." + name().toLowerCase(Locale.ROOT);
}
public abstract void handle(CmdSender sender, String[] args);
public abstract String usage();
public abstract String description();
}
@@ -26,16 +26,19 @@
package ch.andre601.advancedserverlist.core.interfaces.core;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.interfaces.dialogs.IDialog;
import ch.andre601.advancedserverlist.core.interfaces.dialogs.IDialogList;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
import ch.andre601.advancedserverlist.core.profiles.handlers.FaviconHandler;
import org.incendo.cloud.CommandManager;
import java.awt.image.BufferedImage;
import java.nio.file.Path;
public interface PluginCore<F>{
void loadCommands();
void loadEvents();
void loadMetrics();
@@ -48,26 +51,34 @@ public interface PluginCore<F>{
void startScheduler();
AdvancedServerList<F> getCore();
AdvancedServerList<F> core();
Path getFolderPath();
Path folderPath();
PluginLogger getPluginLogger();
PluginLogger pluginLogger();
FaviconHandler<F> getFaviconHandler();
FaviconHandler<F> faviconHandler();
String getPlatformInfo();
CommandManager<CmdSender> commandManager();
String getLoader();
String platformInfo();
String loader();
boolean isPluginEnabled(String plugin);
boolean isDialogsSupported();
F createFavicon(BufferedImage image) throws Exception;
IDialogList.IDialogListBuilder createDialogListBuilder(String title);
IDialog.IDialogBuilder createDialogBuilder(String title, ServerListProfile profile, int profilesPosition);
default boolean isDebugEnabled(){
if(getCore() == null)
if(core() == null)
return false;
return getCore().getFileHandler().getBoolean("debug");
return core().fileHandler().getBoolean("debug");
}
}
@@ -0,0 +1,24 @@
package ch.andre601.advancedserverlist.core.interfaces.dialogs;
import ch.andre601.advancedserverlist.api.objects.NullBool;
import ch.andre601.advancedserverlist.core.commands.CmdProfiles;
import java.util.List;
public interface IDialog<T>{
T createDialog();
interface IDialogBuilder{
IDialogBuilder appendBody(String body);
IDialogBuilder appendTextInput(CmdProfiles.ProfileOption option, String title, String initValue, int maxLength);
IDialogBuilder appendTextbox(CmdProfiles.ProfileOption option, String title, List<String> initValue, int maxLength, int rows);
IDialogBuilder appendNumberRange(CmdProfiles.ProfileOption option, String title, int initValue);
IDialogBuilder appendBoolean(CmdProfiles.ProfileOption option, String title, NullBool initValue);
IDialog<?> build();
}
}
@@ -0,0 +1,16 @@
package ch.andre601.advancedserverlist.core.interfaces.dialogs;
import ch.andre601.advancedserverlist.core.interfaces.commands.CmdSender;
public interface IDialogList{
void show(CmdSender sender);
interface IDialogListBuilder{
IDialogListBuilder appendBody(String body);
IDialogListBuilder appendDialog(IDialog<?> dialog);
IDialogList build();
}
}
@@ -40,23 +40,23 @@ public interface GenericEventWrapper<F, P extends GenericPlayer>{
GenericServerListEvent callEvent(ProfileEntry entry);
void setMaxPlayers(int maxPlayers);
void maxPlayers(int maxPlayers);
void setOnlinePlayers(int onlinePlayers);
void onlinePlayers(int onlinePlayers);
void setMotd(Component component);
void motd(Component component);
void hidePlayers();
void setPlayerCount(String name);
void playerCount(String name);
void setPlayers(List<String> players, P player, GenericServer server);
void players(List<String> players, P player, GenericServer server);
void setPlayersHidden();
void playersHidden();
void setFavicon(F favicon);
void favicon(F favicon);
void setDefaultFavicon();
void defaultFavicon();
void updateEvent();
@@ -64,23 +64,23 @@ public interface GenericEventWrapper<F, P extends GenericPlayer>{
boolean isMaintenanceModeActive();
int getProtocolVersion();
int protocolVersion();
int getOnlinePlayers();
int onlinePlayers();
int getMaxPlayers();
int maxPlayers();
String getPlayerIP();
String playerIP();
String parsePAPIPlaceholders(String text, P player);
String getVirtualHost();
String virtualHost();
PluginCore<F> getPlugin();
PluginCore<F> plugin();
P createPlayer(CachedPlayer player, int protocol);
GenericServer createGenericServer(int playersOnline, int playersMax, String host);
GenericServer createServer(int playersOnline, int playersMax, String host);
default String resolveHost(InetSocketAddress address){
return address == null ? null : address.getHostString();
@@ -49,12 +49,14 @@ import java.util.List;
public class MiniMOTDConfigMigrator{
public static boolean migrate(AdvancedServerList<?> core, CmdSender sender){
core.getPlugin().downloadLibrary("org.spongepowered", "configurate-hocon", "4.1.2");
sender.sendMsg("Loading Configurate-Hocon library. This may take a while...");
core.plugin().downloadLibrary("org.spongepowered", "configurate-hocon", "4.1.2");
sender.sendMsg("<green>Library loaded!");
PluginLogger logger = core.getPlugin().getPluginLogger();
String folder = core.getPlugin().getLoader().equals("velocity") ? "minimotd-velocity" : "MiniMOTD";
PluginLogger logger = core.plugin().pluginLogger();
String folder = core.plugin().loader().equals("velocity") ? "minimotd-velocity" : "MiniMOTD";
Path mainConf = core.getPlugin().getFolderPath().getParent().resolve(folder).resolve("main.conf");
Path mainConf = core.plugin().folderPath().getParent().resolve(folder).resolve("main.conf");
if(!Files.exists(mainConf)){
logger.warn("[<white>Migrator - MiniMOTD</white>] Cannot find a main.conf file in <white>/plugins/%s/</white> folder.", folder);
sender.sendErrorMsg(" -> <red>No main.conf file found.");
@@ -98,7 +100,7 @@ public class MiniMOTDConfigMigrator{
if(config.motdEnabled()){
if(config.motds().size() == 1){
applyMotd(profileBuilder, config.motds().get(0));
applyMotd(profileBuilder, config.motds().getFirst());
}else
if(config.motds().size() > 1){
for(int i = 0; i < config.motds().size(); i++){
@@ -109,7 +111,7 @@ public class MiniMOTDConfigMigrator{
if(config.iconEnabled()){
if(config.motds().size() == 1){
MiniMOTDConfig.Motd motd = config.motds().get(0);
MiniMOTDConfig.Motd motd = config.motds().getFirst();
if(!motd.icon().isEmpty()){
profileBuilder.favicon(motd.icon());
}
@@ -152,7 +154,7 @@ public class MiniMOTDConfigMigrator{
return false;
}
Path profile = core.getPlugin().getFolderPath().resolve("profiles").resolve("minimotd_migrated.yml");
Path profile = core.plugin().folderPath().resolve("profiles").resolve("minimotd_migrated.yml");
if(Files.exists(profile)){
logger.warn("[<white>Migrator - MiniMOTD</white>] Cannot create new file minimotd_migrated.yml. One with the same name is already present.");
sender.sendErrorMsg(" -> <red>File</red> minimotd_migrated.yml <red>already present.");
@@ -99,7 +99,7 @@ public class SLPConfigMigrator{
public static int migrate(AdvancedServerList<?> core, CmdSender sender){
ServerListPlusCore slpCore = ServerListPlusCore.getInstance();
PluginLogger logger = core.getPlugin().getPluginLogger();
PluginLogger logger = core.plugin().pluginLogger();
if(slpCore == null){
logger.warn("[<white>Migrator - ServerListPlus</white>] Cannot migrate ServerListPlus config. ServerListPlus is not active!");
return 0;
@@ -128,7 +128,7 @@ public class SLPConfigMigrator{
}
private static int parseConf(AdvancedServerList<?> core, CmdSender sender, PersonalizedStatusConf.StatusConf conf, Type type){
PluginLogger logger = core.getPlugin().getPluginLogger();
PluginLogger logger = core.plugin().pluginLogger();
if(conf == null){
logger.info("[<white>Migrator - ServerListPlus</white>] No StatusConf found for type <white>%s</white>. Skipping...", type.getName());
sender.sendPrefixedMsg(" -> Not found! Skipping...");
@@ -176,7 +176,7 @@ public class SLPConfigMigrator{
return 0;
}
Path profile = core.getPlugin().getFolderPath().resolve("profiles").resolve(type.getFile());
Path profile = core.plugin().folderPath().resolve("profiles").resolve(type.getFile());
if(Files.exists(profile)){
logger.warn("[<white>Migrator - ServerListPlus</white>] Cannot create new file <white>%s</white>. One with the same name is already present", type.getFile());
sender.sendErrorMsg(" -> <red>File</red> %s <red>already present.", type.getFile());
@@ -223,7 +223,7 @@ public class SLPConfigMigrator{
if(type == Type.PERSONALIZED){
node.node("condition")
.set("${player name} != \"" + core.getFileHandler().getString("Anonymous", "unknownPlayer", "name") + "\"");
.set("${player name} != \"" + core.fileHandler().getString("Anonymous", "unknownPlayer", "name") + "\"");
}else
if(type == Type.BANNED){
node.node("condition")
@@ -25,8 +25,11 @@
package ch.andre601.advancedserverlist.core.parsing;
import ch.andre601.advancedserverlist.core.compat.miniplaceholders.MiniPlaceholdersUtil;
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import java.util.function.Function;
@@ -61,6 +64,17 @@ public class ComponentParser{
return this;
}
public ComponentParser applyMMTags(PluginCore<?> plugin){
if(!plugin.isPluginEnabled("MiniPlaceholders"))
return this;
TagResolver resolver = MiniPlaceholdersUtil.tagResolvers();
Component component = mm.deserialize(this.text, resolver);
this.text = mm.serialize(component);
return this;
}
public Component toComponent(){
return mm.deserialize(text);
}
@@ -77,8 +77,8 @@ public class TextCenterUtil{
private Map<String, FontInfo> loadFontWidths(AdvancedServerList<?> plugin){
try(InputStream stream = plugin.getClass().getResourceAsStream("/char-widths.json.gz")){
if(stream == null){
plugin.getPlugin().getPluginLogger().warn("Internal char-widths.json.gz couldn't be retrieved.");
plugin.getPlugin().getPluginLogger().warn("The <center> Placeholder won't work.");
plugin.plugin().pluginLogger().warn("Internal char-widths.json.gz couldn't be retrieved.");
plugin.plugin().pluginLogger().warn("The <center> Placeholder won't work.");
return new HashMap<>();
}
@@ -87,9 +87,9 @@ public class TextCenterUtil{
return new Gson().fromJson(reader, new TypeToken<Map<String, FontInfo>>(){}.getType());
}catch(IOException ex){
plugin.getPlugin().getPluginLogger().warn("Encountered IOException while loading font widths.");
plugin.getPlugin().getPluginLogger().warn("The <center> Placeholder won't work.");
plugin.getPlugin().getPluginLogger().warn("Cause: <white>%s</white>", ex.getMessage());
plugin.plugin().pluginLogger().warn("Encountered IOException while loading font widths.");
plugin.plugin().pluginLogger().warn("The <center> Placeholder won't work.");
plugin.plugin().pluginLogger().warn("Cause: <white>%s</white>", ex.getMessage());
return new HashMap<>();
}
}
@@ -0,0 +1,81 @@
package ch.andre601.advancedserverlist.core.placeholders;
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.function.Function;
public class DatePlaceholder extends PlaceholderProvider{
public DatePlaceholder(){
super("date");
}
@Override
public @Nullable String parsePlaceholder(@NonNull String placeholder, @NonNull GenericPlayer player, @NonNull GenericServer server){
String[] args = placeholder.split("\\s+");
return switch(args[0]){
case "current" -> {
String format = "dd/MM/yyyy";
if(args.length > 1){
format = format(Arrays.copyOfRange(args, 1, args.length));
}
try{
SimpleDateFormat dateFormat = new SimpleDateFormat(format);
yield dateFormat.format(new Date());
}catch(IllegalArgumentException ex){
yield "ERR: Invalid Date pattern " + format;
}
}
case "isBefore" -> compareDates(args, integer -> integer < 0);
case "isExactly" -> compareDates(args, integer -> integer == 0);
case "isAfter" -> compareDates(args, integer -> integer > 0);
default -> null;
};
}
private String format(String... args){
if(args.length == 0)
return "dd/MM/yyyy";
String format = String.join(" ", args);
if(format.trim().isEmpty()){
return "dd/MM/yyyy";
}else{
return format.trim();
}
}
private String compareDates(String[] args, Function<Integer, Boolean> function){
if(args.length == 1)
return "ERR: No Date provided!";
String targetDateStr = args[1];
String format = "dd/MM/yyyy";
if(args.length > 2){
format = format(Arrays.copyOfRange(args, 2, args.length));
}
SimpleDateFormat dateFormat;
try{
dateFormat = new SimpleDateFormat(format);
Date targetDate = dateFormat.parse(targetDateStr);
int diff = new Date().compareTo(targetDate);
return String.valueOf(function.apply(diff));
}catch(IllegalArgumentException ex){
return "ERR: Invalid Date format provided!";
}catch(ParseException ex){
return "ERR: Received invalid Date " + targetDateStr;
}
}
}
@@ -46,9 +46,18 @@ public record ServerListProfile(int priority, String condition, ProfileEntry def
return true;
ParseWarnCollector collector = new ParseWarnCollector(condition);
PluginLogger logger = core.getPlugin().getPluginLogger();
PluginLogger logger = core.plugin().pluginLogger();
boolean result = core.getParser().evaluate(condition, player, server, collector);
boolean result;
try{
result = core.parser().compileToBoolean(condition, collector, player, server);
}catch(Exception ex){
logger.warn("Encountered an Exception while parsing condition '%s'. Cause: %s", condition, ex.getMessage());
if(core.fileHandler().getBool(false, "debug"))
logger.warn("Stacktrace:", ex);
return false;
}
if(collector.hasWarnings()){
logger.warn("Encountered <white>%d</white> Error(s) while parsing condition for file '<white>%s</white>':", collector.getWarnings().size(), file);
@@ -70,7 +79,7 @@ public record ServerListProfile(int priority, String condition, ProfileEntry def
}
if(profiles.size() == 1)
return profiles.get(0); // No need to run a random for 1 profile.
return profiles.getFirst(); // No need to run a random for 1 profile.
synchronized(random){
return profiles.get(random.nextInt(profiles.size()));
@@ -1,82 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2022-2024 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.core.profiles.conditions;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.reader.PlaceholderTokenReader;
import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.reader.PlaceholderValueReader;
import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.tokens.CustomExpressionTokenizer;
import ch.andre601.expressionparser.ExpressionParserEngine;
import ch.andre601.expressionparser.ParseWarnCollector;
import ch.andre601.expressionparser.operator.Operator;
import ch.andre601.expressionparser.parsers.ExpressionTemplateParser;
import ch.andre601.expressionparser.parsers.ValueReader;
import ch.andre601.expressionparser.templates.ExpressionTemplate;
import ch.andre601.expressionparser.tokens.Token;
import ch.andre601.expressionparser.tokens.readers.TokenReader;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.List;
public class ProfileConditionParser implements ExpressionParserEngine{
private final CustomExpressionTokenizer tokenizer;
private final ExpressionTemplateParser parser;
private ProfileConditionParser(List<TokenReader> tokenReaders, ImmutableMap<Token, Operator> operators, ImmutableList<ValueReader> valueReaders){
this.tokenizer = new CustomExpressionTokenizer(tokenReaders);
this.parser = new ExpressionTemplateParser(operators, valueReaders);
}
public static ProfileConditionParser create(){
return new ProfileConditionParserBuilder().addDefaults()
.addTokenReader(new PlaceholderTokenReader(-20))
.addValueReader(new PlaceholderValueReader())
.build();
}
public boolean evaluate(String text, GenericPlayer player, GenericServer server, ParseWarnCollector collector){
ExpressionTemplate result = parser.parse(tokenizer.parse(text, player, server, collector), collector);
if(result == null)
return false;
return result.returnBooleanExpression().evaluate();
}
@Override
public ExpressionTemplate compile(String s, ParseWarnCollector parseWarnCollector){
return null;
}
private static class ProfileConditionParserBuilder extends ExpressionParserEngine.Builder<ProfileConditionParser>{
@Override
public ProfileConditionParser build(){
return new ProfileConditionParser(this.getTokenReaders(), ImmutableMap.copyOf(this.getOperators()), ImmutableList.copyOf(this.getValueReaders()));
}
}
}
@@ -1,48 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2022-2024 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.reader;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.expressionparser.ParseWarnCollector;
import ch.andre601.expressionparser.tokens.Token;
import ch.andre601.expressionparser.tokens.readers.TokenReader;
import java.text.ParsePosition;
public abstract class CustomTokenReader extends TokenReader{
public CustomTokenReader(int priority){
super(priority);
}
@Override
public Token read(String text, ParsePosition position, ParseWarnCollector collector){
return null;
}
public abstract Token read(String text, ParsePosition position, GenericPlayer player, GenericServer server, ParseWarnCollector collector);
}
@@ -1,92 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2022-2024 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.tokens;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.reader.CustomTokenReader;
import ch.andre601.expressionparser.ParseWarnCollector;
import ch.andre601.expressionparser.tokens.Token;
import ch.andre601.expressionparser.tokens.readers.TokenReader;
import com.google.common.collect.Ordering;
import java.text.ParsePosition;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
public class CustomExpressionTokenizer{
private static final Ordering<TokenReader> TOKEN_READER_ORDERING = Ordering.from(Comparator.comparingInt(TokenReader::getPriority)).reverse();
private final List<TokenReader> tokenReaders;
public CustomExpressionTokenizer(Iterable<TokenReader> tokenReaders){
this.tokenReaders = TOKEN_READER_ORDERING.immutableSortedCopy(tokenReaders);
}
public List<Token> parse(String text, GenericPlayer player, GenericServer server, ParseWarnCollector collector){
ParsePosition position = new ParsePosition(0);
List<Token> tokens = new LinkedList<>();
next_token:
while(true){
while(position.getIndex() < text.length() && Character.isWhitespace(text.charAt(position.getIndex())))
position.setIndex(position.getIndex() + 1);
if(position.getIndex() >= text.length())
break;
for(TokenReader tokenReader : tokenReaders){
Token token;
if(tokenReader instanceof CustomTokenReader customTokenReader){
if(null != (token = customTokenReader.read(text, position, player, server, collector))){
tokens.add(token);
continue next_token;
}
}else{
if(null != (token = tokenReader.read(text, position, collector))){
tokens.add(token);
continue next_token;
}
}
}
if(position.getIndex() >= text.length()){
collector.appendWarningFormatted(
"Current index position (%d) is larger or equal to Text length (%d) for condition '%s'",
position.getIndex(), text.length(), text
);
}else{
collector.appendWarningFormatted(position.getIndex(), "Illegal Token '%c'.", text.charAt(position.getIndex()));
}
break;
}
return tokens;
}
}
@@ -23,27 +23,46 @@
*
*/
package ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.reader;
package ch.andre601.advancedserverlist.core.profiles.conditions.reader;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.tokens.PlaceholderToken;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.PlaceholderToken;
import ch.andre601.advancedserverlist.core.profiles.replacer.StringReplacer;
import ch.andre601.expressionparser.ParseWarnCollector;
import ch.andre601.expressionparser.tokens.Token;
import ch.andre601.expressionparser.tokens.readers.TokenReader;
import java.text.ParsePosition;
public class PlaceholderTokenReader extends CustomTokenReader{
public class PlaceholderTokenReader extends TokenReader{
public PlaceholderTokenReader(int priority){
super(priority);
public PlaceholderTokenReader(){
super(-20);
}
@Override
public Token read(String text, ParsePosition position, GenericPlayer player, GenericServer server, ParseWarnCollector collector){
public Token read(String text, ParsePosition position, ParseWarnCollector collector, Object... args){
GenericPlayer player = null;
GenericServer server = null;
for(Object arg : args){
if(arg instanceof GenericPlayer pl){
player = pl;
}else
if(arg instanceof GenericServer srv){
server = srv;
}
}
if(player == null || server == null){
collector.appendWarning(position.getIndex(), "Received null GenericPlayer or GenericServer instance.");
return null;
}
if(position.getIndex() + 1 < text.length() && text.charAt(position.getIndex()) == '$' && text.charAt(position.getIndex() + 1) == '{'){
position.setIndex(position.getIndex() + 2);
return new PlaceholderToken(StringReplacer.parsePlaceholder(text, position, player, server, collector));
}
return null;
@@ -23,9 +23,9 @@
*
*/
package ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.reader;
package ch.andre601.advancedserverlist.core.profiles.conditions.reader;
import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.tokens.PlaceholderToken;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.PlaceholderToken;
import ch.andre601.expressionparser.ParseWarnCollector;
import ch.andre601.expressionparser.parsers.ExpressionTemplateParser;
import ch.andre601.expressionparser.parsers.ValueReader;
@@ -37,8 +37,8 @@ import java.util.List;
public class PlaceholderValueReader extends ValueReader{
@Override
public ExpressionTemplate read(ExpressionTemplateParser parser, List<Token> tokens, ParseWarnCollector collector){
if(tokens.get(0) instanceof PlaceholderToken){
PlaceholderToken token = (PlaceholderToken)tokens.remove(0);
if(tokens.getFirst() instanceof PlaceholderToken){
PlaceholderToken token = (PlaceholderToken)tokens.removeFirst();
return token.getValue();
}
return null;
@@ -23,7 +23,7 @@
*
*/
package ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.tokens;
package ch.andre601.advancedserverlist.core.profiles.conditions.tokens;
import ch.andre601.expressionparser.expressions.ToBooleanExpression;
import ch.andre601.expressionparser.expressions.ToDoubleExpression;
@@ -1,28 +1,3 @@
/*
* MIT License
*
* Copyright (c) 2022-2024 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.core.profiles.handlers;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
@@ -42,9 +17,12 @@ import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.*;
import java.util.concurrent.*;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
public class FaviconHandler<F>{
@@ -54,183 +32,338 @@ public class FaviconHandler<F>{
private final ThreadPoolExecutor faviconThreadPool;
private final Cache<String, CompletableFuture<F>> faviconCache;
private final Map<String, F> localFavicons = new HashMap<>();
private final HttpClient client = HttpClient.newHttpClient();
private final Random random = new Random();
private final HttpClient client = HttpClient.newHttpClient();
private final Map<String, FaviconPair<F>> localFavicons = new HashMap<>();
public FaviconHandler(AdvancedServerList<F> core){
this.core = core;
this.logger = core.getPlugin().getPluginLogger();
this.faviconThreadPool = createFaviconThreadPool();
this.logger = core.plugin().pluginLogger();
this.faviconThreadPool = createThreadPool();
this.faviconCache = CacheBuilder.newBuilder()
.expireAfterWrite(core.getFileHandler().getLong(1, 1, "faviconCacheTime"), TimeUnit.MINUTES)
.build();
.expireAfterWrite(core.fileHandler().getLong(1, 1, "faviconCacheTime"), TimeUnit.MINUTES)
.build();
loadLocalFavicons();
}
public F getFavicon(String input){
if(localFavicons.size() > 0){
logger.debug(FaviconHandler.class, "Current Local Favicons:");
for(String key : localFavicons.keySet()){
logger.debug(FaviconHandler.class, " - %s", key);
}
public F favicon(String input){
if(localFavicons.containsKey(input.toLowerCase(Locale.ROOT))){
logger.debug(FaviconHandler.class, "Returning local Favicon '%s'.", input);
return localFavicons.get(input.toLowerCase(Locale.ROOT)).favicon();
}
if(input.equalsIgnoreCase("random")){
logger.debug(FaviconHandler.class, "Input matches 'random'. Returning random Favicon...");
return getRandomized();
logger.debug(FaviconHandler.class, "Returning random local Favicon.");
FaviconPair<F> pair = randomFavicon();
if(pair == null){
logger.warn("Cannot return random Favicon. Make sure PNGs are available in the Favicons folder!");
return null;
}
return pair.favicon();
}
if(localFavicons.containsKey(input.toLowerCase(Locale.ROOT))){
logger.debug(FaviconHandler.class, "Input matches local favicon Image. Returning it...");
return localFavicons.get(input.toLowerCase(Locale.ROOT));
CompletableFuture<F> favicon = faviconCache.getIfPresent(input);
if(favicon != null)
return favicon.getNow(null);
logger.debug(FaviconHandler.class, "Processing '%s'", input);
if(input.contains(";")){
String[] parts = input.split(";");
logger.debug(FaviconHandler.class, "Handling multi-part Favicon: ['%s']", String.join("', '", parts));
favicon = CompletableFuture.supplyAsync(() -> {
List<BufferedImage> images = new ArrayList<>();
Arrays.stream(parts).forEach(part -> {
logger.debug(FaviconHandler.class, "Creating BufferedImage for part '%s'...", part);
images.add(create(part));
});
if(images.isEmpty()){
logger.debugWarn(FaviconHandler.class, "Unable to create Multi-part Favicon. List was empty.");
return null;
}
return composeFavicon(images);
}, faviconThreadPool);
}else{
logger.debug(FaviconHandler.class, "Handling Favicon input '%s'...", input);
favicon = CompletableFuture.supplyAsync(() -> {
logger.debug(FaviconHandler.class, "Creating BufferedImage for '%s'...", input);
BufferedImage image = create(input);
if(image == null){
logger.warn("Cannot create Favicon for '%s'. BufferedImage was null!", input);
return null;
}
return createFavicon(image);
}, faviconThreadPool);
}
try{
logger.debug(FaviconHandler.class, "Getting Favicon image from cache...");
return faviconCache.get(input, () -> getFuture(input)).getNow(null);
}catch(ExecutionException ex){
logger.warn("Received ExecutionException while retrieving Favicon for '<white>%s</white>'.", ex, input);
return null;
}
faviconCache.put(input, favicon);
return favicon.getNow(null);
}
public void cleanCache(){
localFavicons.clear();
faviconCache.invalidateAll();
loadLocalFavicons();
}
private CompletableFuture<F> getFuture(String input){
private F composeFavicon(List<BufferedImage> images){
logger.debug(FaviconHandler.class, "Composing Favicon out of %d part(s)...", images.size());
BufferedImage image = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = image.createGraphics();
String strategy = core.fileHandler().getString("resize", "faviconStrategy");
int layer = 0;
for(BufferedImage img : images){
layer++;
logger.debug(FaviconHandler.class, "Applying layer %d...", layer);
if(img == null){
logger.debugWarn(FaviconHandler.class, "Layer %d was null! Skipping...", layer);
continue;
}
switch(strategy.toLowerCase(Locale.ROOT)){
case "center" -> {
int height = img.getHeight();
int width = img.getWidth();
int posHeight = 0;
int posWidth = 0;
if(height != 64){
posHeight = (64 - height) / 2;
}
// 64 - 128 = -64
if(width != 64){
posWidth = (64 - width) / 2;
}
graphics.drawImage(img, posWidth, posHeight, img.getWidth(), img.getHeight(), null);
}
case "none" -> graphics.drawImage(img, 0, 0, img.getWidth(), img.getHeight(), null);
default -> graphics.drawImage(img, 0, 0, 64, 64, null);
}
logger.debug(FaviconHandler.class, "Applied layer %d!", layer);
}
graphics.dispose();
logger.debug(FaviconHandler.class, "Image Composing complete! Creating Favicon instance...");
return createFavicon(image);
}
private BufferedImage create(String input){
if(input.toLowerCase(Locale.ROOT).startsWith("https://")){
logger.debug(FaviconHandler.class, "Resolving URL '%s'...", input);
return CompletableFuture.supplyAsync(() -> fromURL(core, input), this.faviconThreadPool);
logger.debug(FaviconHandler.class, "Handling URL '%s'...", input);
return fromURL(input);
}else
if(input.equalsIgnoreCase("random")){
return CompletableFuture.completedFuture(getRandomized());
logger.debug(FaviconHandler.class, "Returning random local Favicon...");
FaviconPair<F> pair = randomFavicon();
return pair == null ? null : pair.image();
}else
if(input.toLowerCase(Locale.ROOT).endsWith(".png")){
logger.debug(FaviconHandler.class, "Resolving image file '%s'...", input);
return CompletableFuture.completedFuture(localFavicons.get(input.toLowerCase(Locale.ROOT)));
logger.debug(FaviconHandler.class, "Returning local Favicon '%s'...", input);
FaviconPair<F> pair = localFavicons.get(input.toLowerCase(Locale.ROOT));
return pair == null ? null : pair.image();
}else{
logger.debug(FaviconHandler.class, "Resolving Name/UUID as https://mc-heads.net/avatar/%s/64...", input);
return CompletableFuture.supplyAsync(() -> fromURL(core, "https://mc-heads.net/avatar/" + input + "/64"), this.faviconThreadPool);
logger.debug(FaviconHandler.class, "Handling Player Name/UUID '%s'...", input);
return fromURL("https://mc-heads.net/avatar/" + input + "/64");
}
}
private F getRandomized(){
logger.debug(FaviconHandler.class, "Obtaining random Favicon...");
private FaviconPair<F> randomFavicon(){
if(localFavicons.isEmpty())
return null;
List<F> faviconList = List.copyOf(localFavicons.values());
List<FaviconPair<F>> favicons = new ArrayList<>(localFavicons.values());
if(favicons.isEmpty())
return null;
// Don't use Random for just one entry.
if(faviconList.size() == 1)
return faviconList.get(0);
if(favicons.size() == 1)
return favicons.getFirst();
synchronized(random){
return faviconList.get(random.nextInt(faviconList.size()));
return favicons.get(random.nextInt(favicons.size()));
}
}
private void loadLocalFavicons(){
this.localFavicons.clear();
logger.info("[-] Loading local image files as Favicons...");
logger.info("[-] Loading local images as Favicons...");
Path folder = core.getPlugin().getFolderPath().resolve("favicons");
Path folder = core.plugin().folderPath().resolve("favicons");
if(!Files.exists(folder)){
try{
Files.createDirectories(folder);
logger.debugSuccess(FaviconHandler.class, "Created favicons folder.");
logger.debugSuccess(FaviconHandler.class, "Created favicons folder");
}catch(IOException ex){
logger.failure("Cannot create favicons folder. Encountered an IOException.", ex);
logger.warn("Encountered IOException while creating favicons folder.", ex);
return;
}
}
try(Stream<Path> pathStream = Files.list(folder)){
pathStream.filter(Files::isRegularFile)
.filter(file -> file.getFileName().toString().endsWith(".png"))
.forEach(this::loadFile);
try(Stream<Path> stream = Files.list(folder)){
stream.filter(Files::isRegularFile)
.filter(file -> file.getFileName().toString().endsWith(".png"))
.forEach(this::loadFile);
logger.success("Loaded <white>%d</white> local Favicons.", localFavicons.size());
logger.success("Loaded <white>%s</white> local Favicon(s).", localFavicons.size());
}catch(IOException ex){
logger.failure("Cannot load files from favicons folder.", ex);
if(core.fileHandler().getBool(false, "debug")){
logger.warn("Encountered IOException while loading local favicons.", ex);
}else{
logger.warn("Encountered IOException while loading local favicons. Reason: %s", ex.getMessage());
}
}
}
private void loadFile(Path path){
try(InputStream stream = Files.newInputStream(path)){
F favicon = createFavicon(stream);
if(favicon == null){
logger.failure("Cannot create Favicon from file '<white>%s</white>'. Received Favicon was null.", path.getFileName().toString());
BufferedImage image = createBufferedImage(stream);
if(image == null)
return;
}
localFavicons.put(path.getFileName().toString().toLowerCase(Locale.ROOT), favicon);
logger.debugSuccess(FaviconHandler.class, "Loaded file '<white>%s</white>' as Favicon.", path.getFileName().toString());
F favicon = core.plugin().createFavicon(image);
if(favicon == null)
return;
localFavicons.put(path.getFileName().toString().toLowerCase(Locale.ROOT), new FaviconPair<>(image, favicon));
}catch(IOException ex){
logger.failure("Cannot create Favicon from file '<white>%s</white>'. Encountered IOException.", ex);
if(core.fileHandler().getBool(false, "debug")){
logger.warn("Encountered IOException while creating a Favicon from '%s'.", ex, path.getFileName().toString());
}else{
logger.warn("Encountered IOException while creating a Favicon from '%s'. Reason: %s", path.getFileName().toString(), ex.getMessage());
}
}catch(Exception ex){
if(core.fileHandler().getBool(false, "debug")){
logger.warn("Encountered Exception while creating a Favicon from '%s'.", ex, path.getFileName().toString());
}else{
logger.warn("Encountered Exception while creating a Favicon from '%s'. Reason: %s", path.getFileName().toString(), ex.getMessage());
}
}
}
private F fromURL(AdvancedServerList<F> core, String url){
private BufferedImage fromURL(String url){
try{
logger.debug(FaviconHandler.class, "Creating Request for URL '%s'...", url);
logger.debug(FaviconHandler.class, "Creating request for URL '%s'...", url);
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI(url))
.header("User-Agent", "AdvancedServerList-" + core.getPlugin().getLoader() + "/" + core.getVersion())
.build();
.uri(new URI(url))
.header("User-Agent", "AdvancedServerList-" + core.plugin().loader() + "/" + core.version())
.build();
try(InputStream stream = client.send(request, HttpResponse.BodyHandlers.ofInputStream()).body()){
return createFavicon(stream);
}catch(InterruptedException | IOException ex){
logger.warn("Cannot create Favicon from URL '<white>%s</white>'. Encountered an Exception.", ex, url);
return createBufferedImage(stream);
}catch(IOException ex){
if(core.fileHandler().getBool(false, "debug")){
logger.warn("Encountered IOException while performing a web request for '%s'.", ex, url);
}else{
logger.warn("Encountered IOException while performing a web request for '%'. Reason: %s", url, ex.getMessage());
}
return null;
}catch(InterruptedException ex){
if(core.fileHandler().getBool(false, "debug")){
logger.warn("Encountered an InterruptedException while performing a web request for '%s'.", ex, url);
}else{
logger.warn("Encountered an InterruptedException while performing a web request for '%s'. Reason: %s", url, ex.getMessage());
}
return null;
}
}catch(URISyntaxException ex){
logger.warn("Cannot create Favicon from URL '<white>%s</white>'. Encountered a URISyntaxException.", ex, url);
if(core.fileHandler().getBool(false, "debug")){
logger.warn("Encountered an URISyntaxException while performing a web request for '%s'.", ex, url);
}else{
logger.warn("Encountered an URISyntaxException while performing a web request for '%s'. Reason: %s", url, ex.getMessage());
}
return null;
}
}
private F createFavicon(InputStream stream){
private BufferedImage createBufferedImage(InputStream stream){
logger.debug(FaviconHandler.class, "Creating BufferedImage from InputStream...");
try{
logger.debug(FaviconHandler.class, "Creating BufferedImage from InputStream...");
BufferedImage original = ImageIO.read(stream);
if(original == null){
logger.warn("Cannot create Favicon. Received null BufferedImage.");
logger.warn("Cannot create Favicon. Received a null BufferedImage!");
return null;
}
// Don't waste resources resizing images already having right size.
if(original.getWidth() == 64 && original.getHeight() == 64){
logger.debug(FaviconHandler.class, "BufferedImage is 64x64 pixels. No resizing needed.");
return core.getPlugin().createFavicon(original);
logger.debug(FaviconHandler.class, "Check image size for possible resizing...");
if(original.getHeight() == 64 && original.getWidth() == 64){
logger.debug(FaviconHandler.class, "Image is 64x64. No resizing needed!");
return original;
}
logger.debug(FaviconHandler.class, "Resizing image to 64x64 pixels...");
logger.debug(FaviconHandler.class, "Applying Favicon resize strategy...");
BufferedImage image = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics2D = image.createGraphics();
Graphics2D graphics = image.createGraphics();
graphics2D.drawImage(original, 0, 0, 64, 64, null);
graphics2D.dispose();
String resizeMode = core.fileHandler().getString("resize", "faviconStrategy");
switch(resizeMode.toLowerCase(Locale.ROOT)){
case "center" -> {
int height = original.getHeight();
int width = original.getWidth();
int posHeight = 0;
int posWidth = 0;
if(height != 64){
posHeight = (64 - height) / 2;
}
if(width != 64){
posWidth = (64 - width) / 2;
}
graphics.drawImage(original, posWidth, posHeight, original.getWidth(), original.getHeight(), null);
}
case "none" -> graphics.drawImage(original, 0, 0, original.getWidth(), original.getHeight(), null);
default -> graphics.drawImage(original, 0, 0, 64, 64, null);
}
logger.debug(FaviconHandler.class, "Image resized! Returning Favicon...");
return core.getPlugin().createFavicon(image);
}catch(Exception ex){
logger.warn("Unable to create Favicon. Received Exception.", ex);
graphics.dispose();
logger.debug(FaviconHandler.class, "Image resized. Returning it...");
return image;
}catch(IOException ex){
logger.warn("Encountered an IOException while processing Favicon!", ex);
return null;
}
}
private ThreadPoolExecutor createFaviconThreadPool(){
private F createFavicon(BufferedImage image){
try{
return core.plugin().createFavicon(image);
}catch(Exception ex){
if(core.fileHandler().getBool(false, "debug")){
logger.warn("Encountered an Exception while trying to create a Favicon instance.", ex);
}else{
logger.warn("Encountered an Exception while trying to create a Favicon instance. Reason: %s", ex.getMessage());
}
return null;
}
}
private ThreadPoolExecutor createThreadPool(){
return new ThreadPoolExecutor(3, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024), r -> {
Thread t = new Thread(r, "AdvancedServerList-FaviconThread");
t.setDaemon(true);
return t;
});
}
private record FaviconPair<F>(BufferedImage image, F favicon){}
}
@@ -61,8 +61,8 @@ public class PlayerHandler{
public PlayerHandler(AdvancedServerList<?> core){
this.core = core;
this.logger = core.getPlugin().getPluginLogger();
this.cache = core.getPlugin().getFolderPath().resolve("playercache.json");
this.logger = core.plugin().pluginLogger();
this.cache = core.plugin().folderPath().resolve("playercache.json");
}
public void load(){
@@ -71,7 +71,7 @@ public class PlayerHandler{
return;
}
if(core.getFileHandler().getBoolean("disableCache")){
if(core.fileHandler().getBoolean("disableCache")){
logger.info("'disableCache' is set to true. Skipping playercache.json loading...");
return;
}
@@ -103,7 +103,7 @@ public class PlayerHandler{
return;
}
if(core.getFileHandler().getBoolean("disableCache")){
if(core.fileHandler().getBoolean("disableCache")){
logger.debug(PlayerHandler.class, "[-] 'disable_cache' is set to true. Not saving cached players.");
return;
}
@@ -117,14 +117,14 @@ public class PlayerHandler{
}
public void addPlayer(String ip, String name, UUID uuid){
if(contains(ip) || core.getFileHandler().getBoolean("disableCache"))
if(contains(ip) || core.fileHandler().getBoolean("disableCache"))
return;
cachedPlayers.add(new CachedPlayer(ip, name, uuid));
}
public CachedPlayer getCachedPlayer(String key){
if(!contains(key) || core.getFileHandler().getBoolean("disableCache"))
if(!contains(key) || core.fileHandler().getBoolean("disableCache"))
return getDefaultPlayer();
for(CachedPlayer player : cachedPlayers){
@@ -164,8 +164,8 @@ public class PlayerHandler{
return (defaultPlayer = new CachedPlayer(
"0.0.0.0",
core.getFileHandler().getString("Anonymous", "unknownPlayer", "name"),
convertToUUID(core.getFileHandler().getString(defaultUUID.toString(), "unknownPlayer", "uuid"))
core.fileHandler().getString("Anonymous", "unknownPlayer", "name"),
convertToUUID(core.fileHandler().getString(defaultUUID.toString(), "unknownPlayer", "uuid"))
));
}
@@ -78,7 +78,7 @@ public class ProfileManager{
}
public static ServerListProfile resolveProfile(AdvancedServerList<?> core, GenericPlayer player, GenericServer server){
for(ServerListProfile profile : core.getFileHandler().getProfiles()){
for(ServerListProfile profile : core.fileHandler().profiles()){
if(profile.isInvalidProfile())
continue;
@@ -120,20 +120,13 @@ public class ProfileManager{
}
public static boolean checkOption(Object obj){
if(obj == null)
return false;
return switch(obj){
case List<?> list -> !list.isEmpty(); // Check if list isn't empty
case String str -> !str.isEmpty(); // Check if String is not empty
case NullBool nb -> nb.getOrDefault(false); // Return NullBool's value
case null, default -> false;
};
if(obj instanceof List<?> list){
return !list.isEmpty(); // Check if list isn't empty
}else
if(obj instanceof String str){
return !str.isEmpty(); // Check if String is not empty
}else
if(obj instanceof NullBool nb){
return nb.getOrDefault(false); // Return NullBool's value
}
return false;
}
private static <T> T resolve(ProfileEntry defProfile, ProfileEntry profile, Function<ProfileEntry, T> function){
@@ -30,13 +30,18 @@ import ch.andre601.advancedserverlist.api.PlaceholderProvider;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.tokens.PlaceholderToken;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.PlaceholderToken;
import ch.andre601.expressionparser.ParseWarnCollector;
import java.text.ParsePosition;
import java.util.function.Function;
public class StringReplacer{
public static StringModifier modifier(String text){
return new StringModifier(text);
}
public static String replace(String input, GenericPlayer player, GenericServer server){
if(input == null)
return null;
@@ -63,7 +68,7 @@ public class StringReplacer{
}
private static String parsePlaceholder0(String input, ParsePosition position, GenericPlayer player, GenericServer server, ParseWarnCollector collector){
AdvancedServerListAPI api = AdvancedServerList.getApi();
AdvancedServerListAPI api = AdvancedServerList.api();
int index = position.getIndex();
StringBuilder identifier = new StringBuilder();
@@ -148,4 +153,22 @@ public class StringReplacer{
return replacement;
}
public static class StringModifier{
private String value;
public StringModifier(String value){
this.value = value;
}
public StringModifier modify(Function<String, String> function){
this.value = function.apply(value);
return this;
}
public String asString(){
return value;
}
}
}
+13
View File
@@ -66,6 +66,19 @@ printBanner: true
#
faviconCacheTime: 1
#
# Sets how AdvancedServerList should handle the size and positioning of images for favicons.
#
# Available options:
#
# - resize: Resizes the image to 64x64 pixels if it isn't these dimensions already.
# - center: Doesn't resize the image, but instead tries to center it as good as possible in a 64x64 area.
# - none: Doesn't resize the image and leaves it positioned at x 0 and y 0 (top-left) of the 64x64 area.
#
# Setting any not listed option or leaving this blank/removing it sets the option to resize.
#
faviconResizeStrategy: 'resize'
#
# Options to disable while the Maintenance Plugin is present and Maintenance Mode is active.
#
+43 -37
View File
@@ -2,7 +2,7 @@
# Set the priority of this profile.
# If there are multiple profiles (files) will they be sorted by priority, starting with highest.
#
# Read more: https://asl.andre601.ch/profiles/#priority
# Read more: https://asl.andre601.ch/profiles/options/priority/
#
priority: 0
@@ -10,11 +10,11 @@ priority: 0
# Set condition(s) that should return true to display this profile.
# This allows you to create profiles to only show for specific scenarios (i.e. player is banned).
#
# Multiple conditions can be chaing with the keywords 'and', 'or', or their counterparts '&&' and '||'.
# 'AND'/'&&' and 'OR'/'||' can be used to chain together multiple conditions.
#
# No condition or an empty condition will be treated as true by default.
# Removing the condition option or setting it to an empty String will be treated as a true condition being returned.
#
# Read more: https://asl.andre601.ch/profiles/#condition
# Read more: https://asl.andre601.ch/profiles/options/condition
#
condition: ''
@@ -30,7 +30,7 @@ condition: ''
#
# When not present or empty (profiles: []), no profiles will be used and global options from the file will be used instead.
#
# Read more: https://asl.andre601.ch/profiles/#profiles
# Read more: https://asl.andre601.ch/profiles/options/profiles
#
profiles:
- motd: ['<aqua>Line A', '<gold>Line B']
@@ -40,14 +40,17 @@ profiles:
#
# Set the "Message of the day" to display.
# This option supports RGB colors for 1.16+ Servers and clients, including gradients using MiniMessage's <gradient>
# option.
# This option supports RGB colors for 1.16+ Servers and clients. More advanced MiniMessage features such as <gradient>
# can be used for further styling the text.
#
# <center> can be put at the start of a line to have AdvancedServerList automatically center the text as good as
# possible.
#
# Only the first two lines will be considered and any additional one is ignored.
#
# When not present or empty (motd: []), no MOTD will be set.
#
# Read more: https://asl.andre601.ch/profiles/#motd
# Read more: https://asl.andre601.ch/profiles/options/motd
#
motd:
- '<grey>First Line'
@@ -55,33 +58,36 @@ motd:
#
# Set the Favicon (image on the left of a server entry) to display.
# This option supports three types of inputs:
# This option supports three types of input:
#
# 1. Name, including .png extension, pointing to an image located in the plugin's favicons folder.
# 2. "random" to display a random PNG from the favicons folder.
# 1. Filename, including the .png extension, matching a filename in the favicons folder of the plugin.
# - 'random' can be used to have AdvancedServerList select a random image from the favicons folder every time
# the server is pinged.
# 3. URL pointing to an image.
# 4. Player name, UUID or placeholders returning one of these.
# https://mc-heads.net will be used to retrieve an image of the provided name/UUID.
# 4. Player name, UUID or a placeholder that returns either.
# - https://mc-heads.net will be used to retrieve an image of the provided name/UUID.
#
# Note on favicons not showing:
# AdvancedServerList has to create a Favicon object for the Proxy/Server to use which takes time to do so.
# To not delay the Proxy/Server from being displayed (increase the loading time on the player end) is this creation
# done asynchronously for URL/Player name/UUID. This has the downside that the Proxy/Server may be done handling the
# ping before the Favicon is created, causing none to be displayed.
# Note that this won't happen with local favicons, as those are created during the plugin's startup.
#
# Any images above or below 64x64 pixels will be automatically resized to these dimensions.
# Any image below or above 64x64 pixels will be resized according to the faviconResizeMode option in the config.yml.
#
# If not present or empty (favicon: ''), defaults to not modifying the favicon.
#
# Read more: https://asl.andre601.ch/profiles/#favicon
# Read more: https://asl.andre601.ch/profiles/options/favicon
#
# "The favicon isn't showing!"
# This is a side effect of using either URLs or player names/UUIDs for the favicon.
# These types require AdvancedServerList to create a new favicon object from scratch. This action takes a significant
# amount of time, which can cause the server to show with a huge ping.
# To avoid this is this action performed asynchronously. This means that the plugin doesn't wait for the favicon to
# be created before continuing, which can result in no favicon being available when it is done handling the ping.
# This should only happen for the first ping of a player and subsequent pings should display the favicon, if the
# pings are done within the configured cache time (See 'faviconCacheTime' in the config.yml).
#
favicon: ''
#
# Contains options for modifying the player count (Text displaying <online>/<max>).
#
# Read more: https://asl.andre601.ch/profiles/#playercount
# Read more: https://asl.andre601.ch/profiles/options/playercount
#
playerCount:
#
@@ -91,7 +97,7 @@ playerCount:
#
# If not present, defaults to false.
#
# Read more: https://asl.andre601.ch/profiles/#hideplayers
# Read more: https://asl.andre601.ch/profiles/options/playercount/hideplayers
#
hidePlayers: false
#
@@ -100,17 +106,17 @@ playerCount:
#
# If not present, defaults to false.
#
# Read more: https://asl.andre601.ch/profiles/#hideplayershover
# Read more: https://asl.andre601.ch/profiles/options/playercount/hideplayershover
#
hidePlayersHover: false
#
# Set the lines shown when the player hovers over the player count.
# Unlike the MOTD option does this one only support basic color and formatting codes. Any RGB colors will be
# downsampled to the nearest matching color.
# down-sampled to the nearest matching color.
#
# If not present or empty (hover: []), defaults to not modifying the hover.
#
# Read more: https://asl.andre601.ch/profiles/#hover
# Read more: https://asl.andre601.ch/profiles/options/playercount/hover
#
hover:
- '<aqua>Line 1'
@@ -124,7 +130,7 @@ playerCount:
#
# If not present or empty (text: ''), defaults to not modifying the text.
#
# Read more: https://asl.andre601.ch/profiles/#text
# Read more: https://asl.andre601.ch/profiles/options/playercount/text
#
text: ''
#
@@ -133,7 +139,7 @@ playerCount:
#
# Using this feature will modify the ${server playersMax} placeholder, except for usage in the condition option.
#
# Read more: https://asl.andre601.ch/profiles/#extraplayers
# Read more: https://asl.andre601.ch/profiles/options/playercount/extraplayers
#
extraPlayers:
#
@@ -143,7 +149,7 @@ playerCount:
#
# If not present, defaults to false.
#
# Read more: https://asl.andre601.ch/profiles/#extraplayers-enabled
# Read more: https://asl.andre601.ch/profiles/options/playercount/extraplayers#enabled
#
enabled: false
#
@@ -155,7 +161,7 @@ playerCount:
#
# If not present, defaults to 0.
#
# Read more: https://asl.andre601.ch/profiles/#extraplayers-amount
# Read more: https://asl.andre601.ch/profiles/options/playercount/extraplayers#amount
#
amount: 0
#
@@ -163,7 +169,7 @@ playerCount:
#
# Using this feature will modify the ${server playersMax} placeholder, except for usage in the condition option.
#
# Read more: https://asl.andre601.ch/profiles/#maxplayers
# Read more: https://asl.andre601.ch/profiles/options/playercount/maxplayers
#
maxPlayers:
#
@@ -172,7 +178,7 @@ playerCount:
#
# If not present, defaults to false.
#
# Read more: https://asl.andre601.ch/profiles/#maxplayers-enabled
# Read more: https://asl.andre601.ch/profiles/options/playercount/maxplayers#enabled
#
enabled: false
#
@@ -181,13 +187,13 @@ playerCount:
#
# If not present, defaults to 0.
#
# Read more: https://asl.andre601.ch/profiles/#maxplayers-amount
# Read more: https://asl.andre601.ch/profiles/options/playercount/maxplayers#amount
#
amount: 0
#
# The onlinePlayers feature allows to set a number to use as the online player count to display.
#
# Read more: https://asl.andre601.ch/profiles/#onlineplayers
# Read more: https://asl.andre601.ch/profiles/options/playercount/onlineplayers
#
onlinePlayers:
#
@@ -196,7 +202,7 @@ playerCount:
#
# If not present, defaults to false.
#
# Read more: https://asl.andre601.ch/profiles/#onlineplayers-enabled
# Read more: https://asl.andre601.ch/profiles/options/playercount/onlineplayers#enabled
#
enabled: false
#
@@ -205,6 +211,6 @@ playerCount:
#
# If not present, defaults to current online player count.
#
# Read more: https://asl.andre601.ch/profiles/#onlineplayers-amount
# Read more: https://asl.andre601.ch/profiles/options/playercount/onlineplayers#amount
#
amount: 0
@@ -25,27 +25,35 @@
package ch.andre601.advancedserverlist.core;
import ch.andre601.advancedserverlist.core.profiles.conditions.ProfileConditionParser;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.expressionparser.DefaultExpressionParserEngine;
import ch.andre601.expressionparser.ParseWarnCollector;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import java.util.Map;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ExpressionParserTests{
private final ProfileConditionParser parser = ProfileConditionParser.create();
private final DefaultExpressionParserEngine parser = DefaultExpressionParserEngine.createDefault();
private final DummyPluginLogger logger = new DummyPluginLogger();
private final Map<String, Boolean> testValuesMap = getTestMap();
private final TestPlayer player = new TestPlayer();
private final TestServer server = new TestServer();
@Test
public void testBasicExpressionResults(){
for(Map.Entry<String, Boolean> values : testValuesMap.entrySet()){
logger.info("TEST[expression=\"%s\", expected=%b]", values.getKey(), values.getValue());
ParseWarnCollector collector = new ParseWarnCollector(values.getKey());
boolean result = parser.evaluate(values.getKey(), null, null, collector);
boolean result = parser.compileToBoolean(values.getKey(), collector, player, server);
logger.info("Result (Output, Expected?): %b, %b", result, (result == values.getValue()));
assertEquals(result, values.getValue());
@@ -66,4 +74,38 @@ public class ExpressionParserTests{
"false or 100 < 10", false
);
}
private static class TestPlayer implements GenericPlayer{
@Override
public @NonNull String getName(){
return "Lorem";
}
@Override
public int getProtocol(){
return 1;
}
@Override
public @NonNull UUID getUUID(){
return UUID.randomUUID();
}
}
private static class TestServer implements GenericServer{
@Override
public int getPlayersOnline(){
return 10;
}
@Override
public int getPlayersMax(){
return 20;
}
@Override
public @Nullable String getHost(){
return "play.example.com";
}
}
}
+15
View File
@@ -0,0 +1,15 @@
def on_pre_page_macros(env):
footer = """
{% if page.meta and page.meta.api %}
{% include 'api_docs/constructor_summaries.md' %}
{% include 'api_docs/nested_class_summaries.md' %}
{% include 'api_docs/enum_summaries.md' %}
{% include 'api_docs/method_summaries.md' %}
{% include 'api_docs/constructor_details.md' %}
{% include 'api_docs/enum_details.md' %}
{% include 'api_docs/method_details.md' %}
{% endif %}
"""
env.markdown += footer
+23
View File
@@ -0,0 +1,23 @@
import re
from mkdocs.config.defaults import MkDocsConfig
from mkdocs.structure.files import Files
from mkdocs.structure.pages import Page
from re import Match
def on_page_markdown(
markdown: str, *, page: Page, config: MkDocsConfig, files: Files
):
def replace(match: Match):
platform = match.group(1)
if platform == "paper": return "[:fontawesome-solid-paper-plane:](https://papermc.io){ .md-button .md-button--icon title=\"PaperMC\" target=\"_blank\" rel=\"noopener\" }"
elif platform == "bungee": return "[:simple-spigotmc:](https://spigotmc.org){ .md-button .md-button--icon title=\"BungeeCord\" target=\"_blank\" rel=\"noopener\" }"
elif platform == "velocity": return "[:simple-velocity:](https://velocitypowered.com){ .md-button .md-button--icon title=\"Velocity\" target=\"_blank\" rel=\"noopener\" }"
elif platform == "proxies": return "[:simple-spigotmc:](https://spigotmc.org){ .md-button .md-button--icon title=\"BungeeCord\" target=\"_blank\" rel=\"noopener\" } [:simple-velocity:](https://velocitypowered.com){ .md-button .md-button--icon title=\"Velocity\" target=\"_blank\" rel=\"noopener\" }"
elif platform == "all": return "[:fontawesome-solid-paper-plane:](https://papermc.io){ .md-button .md-button--icon title=\"PaperMC\" target=\"_blank\" rel=\"noopener\" } [:simple-spigotmc:](https://spigotmc.org){ .md-button .md-button--icon title=\"BungeeCord\" target=\"_blank\" rel=\"noopener\" } [:simple-velocity:](https://velocitypowered.com){ .md-button .md-button--icon title=\"Velocity\" target=\"_blank\" rel=\"noopener\" }"
else: return platform
return re.sub(
r"<!-- icon:(paper|bungee|velocity|proxies|all) -->",
replace, markdown, flags = re.I | re.M
)
-35
View File
@@ -1,35 +0,0 @@
{% extends "base.html" %}
{% import "api-objects/info-template.html" as info %}
{% block content %}
{% include "partials/content.html" %}
{% if page.meta %}
<!--
Object summaries
-->
{% if page.meta.constructors %}
{{ info.summary('Constructor Summary', page.meta.constructors, details_type='constructor', defaults=page.meta.defaults) }}
{% endif %}
{% if page.meta.classes %}
{{ info.summary('Class Summary', page.meta.classes, details_type='class', defaults=page.meta.defaults) }}
{% endif %}
{% if page.meta.enums %}
{{ info.summary('Enum Constant Summary', page.meta.enums, details_type='enum', defaults=page.meta.defaults) }}
{% endif %}
{% if page.meta.methods or page.meta.inherits %}
{{ info.summary('Method Summary', page.meta.methods, page.meta.inherits, details_type='method', defaults=page.meta.defaults) }}
{% endif %}
<!--
Detailed object info
-->
{% if page.meta.constructors %}
{{ info.info('Constructor Detail', page.meta.constructors, 'constructor', defaults=page.meta.defaults) }}
{% endif %}
{% if page.meta.enums %}
{{ info.info('Enum Constant Detail', page.meta.enums, 'enum', defaults=page.meta.defaults) }}
{% endif %}
{% if page.meta.methods %}
{{ info.info('Method Detail', page.meta.methods, 'method', defaults=page.meta.defaults)}}
{% endif %}
{% endif %}
{% endblock %}
@@ -1,228 +0,0 @@
{% macro summary(title, summaries, inherits, details_type = 'unknown', defaults = []) -%}
<h2 id="{{ title | lower() | replace(' ', '-') | e}}">
{{ title }}
</h2>
<div class="md-typeset__scrollwrap">
<div class="md-typeset__table">
{% if summaries %}
<table>
<thead>
<tr>
{% if details_type and details_type == "method" %}
<th>Modifier and Type</th>
{% endif %}
<th>{{ details_type | capitalize() }}</th>
<th>Description</th>
</tr>
</thead>
<tbody>
{% for summary in summaries %}
<tr>
{% set modifiers = namespace(name = "") %}
{% for attribute in summary.attributes %}
{% set modifiers.name = modifiers.name ~ attribute %}
{% if loop.revindex > 0 %}{% set modifiers.name = modifiers.name ~ " " %}{% endif %}
{% endfor %}
{% if modifiers.name %}{% set modifiers.name = modifiers.name ~ " " %}{% endif %}
{% if summary.type and summary.type.name %}
{% set modifiers.name = modifiers.name ~ summary.type.name %}
{% elif defaults and defaults.type and defaults.type.name %}
{% set modifiers.name = modifiers.name ~ defaults.type.name %}
{% else %}
{% set modifiers.name = modifiers.name ~ "void" %}
{% endif %}
{% set desc = "" %}
{% if summary.deprecated %}
{% set desc = "<api__deprecated></api__deprecated><br>" ~ summary.deprecated.split("<br>")[0] %}
{% elif summary.description %}
{% set desc = summary.description.split("<br>")[0] %}
{% endif %}
{% set final_name = namespace(name = "") %}
{% if details_type and details_type == "enum" %}
{% set final_name.name = summary.name | upper() %}
{% else %}
{% set final_name.name = summary.name ~ "(" %}
{% endif %}
{% for parameter in summary.parameters %}
{% set final_name.name = final_name.name ~ parameter.type %}
{% if loop.revindex > 1 %}{% set final_name.name = final_name.name ~ ", " %}{% endif %}
{% endfor %}
{% if details_type != "enum" %}
{% set final_name.name = final_name.name ~ ")" %}
{% endif %}
{% if details_type and details_type == "method" %}
{% if modifiers.name %}
<td><code>{{ modifiers.name }}</code></td>
{% else %}
<td></td>
{% endif %}
{% endif %}
<td>
{% if summary.link %}
<a href="{{ summary.link }}"><code>{{ final_name.name | e}}</code></a>
{% else %}
<a href="#{{ final_name.name | lower() | replace(' ', '-') | e }}"><code>{{ final_name.name | e}}</code></a>
{% endif %}
</td>
<td>{{ desc }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% if inherits %}
{% for classpath, inherit in inherits.items() %}
<table>
<thead>
<tr>
<th>Methods inherited from <a href="{{ inherit.link }}"><code>{{ classpath }}</code></a></th>
</tr>
</thead>
<tbody>
<tr>
<td>
{% for inh in inherit.list %}
<a href="{{ inherit.link }}#{{ inh | lower() | replace(' ', '-') | e }}">
<code>{{- inh | e -}}</code>
</a>
{%- if loop.revindex > 1 %}, {% endif -%}
{% endfor %}
</td>
</tr>
</tbody>
</table>
{% endfor %}
{% endif %}
</div>
</div>
{%- endmacro %}
{% macro info(title, details, details_type = 'unknown', defaults = []) -%}
<h2 id="{{ title | lower() | replace(' ', '-') | e}}">
{{ title }}
</h2>
{% for detail in details %}
<div class="api-detail">
{% set final_name = namespace(name = "") %}
{% if details_type == "enum" %}
{% set final_name.name = detail.name | upper() %}
{% else %}
{% set final_name.name = detail.name ~ "(" %}
{% endif %}
{% for parameter in detail.parameters %}
{% set final_name.name = final_name.name ~ parameter.type %}
{% if loop.revindex > 1 %}{% set final_name.name = final_name.name ~ ", " %}{% endif %}
{% endfor %}
{% if details_type != "enum" %}
{% set final_name.name = final_name.name ~ ")" %}
{% endif %}
<h3 id="{{ final_name.name | lower() | replace(' ', '-') | e }}">
{% if detail.deprecated %}
<api__deprecated></api__deprecated>
{% endif %}
{% for attribute in detail.attributes %}
<api__{{- attribute -}}></api__{{- attribute -}}>
{% endfor %}
{% if enum %}
<api__static></api__static> <api__final></api__final>
{% endif %}
{% if detail.type and details_type == "method" %}
{% if detail.type.link %}
<a href="{{ detail.type.link }}" class="api-type__{{- detail.type.type | default('primitive') -}}">{{ detail.type.name | default('void') | e }}</a>
{% else %}
<span class="api-type__{{- detail.type.type | default('primitive') -}}">{{ detail.type.name | default('void') | e }}</span>
{% endif %}
{% elif defaults and defaults.type and details_type == "method" %}
{% if defaults.type.link %}
<a href="{{ defaults.type.link }}" class="api-type__{{- defaults.type.type | default('primitive') -}}">{{ defaults.type.name | default('void') | e }}</a>
{% else %}
<span class="api-type__{{- defaults.type.type | default('primitive') -}}">{{ defaults.type.name | default('void') }}</span>
{% endif %}
{% elif details_type == "method" %}
<span class="api-type__primitive">void</span>
{% endif %}
<code>{{ final_name.name | e }}</code><a href="#{{ final_name.name | lower() | replace(' ', '-') | e }}" class="headerlink" title="Permanent link"></a>
</h3>
{% if detail.deprecated %}
<div class="admonition deprecated">
<div class="admonition-title">Deprecation Warning</div>
<p>
{{ detail.deprecated }}
</p>
</div>
{% endif %}
{% if detail.description %}
<p class="api-detail__description">
{{ detail.description }}
</p>
{% endif %}
{% if detail.parameters %}
<h4 id="{{ detail.name | lower() | replace(' ', '-') | e }}-parameters">
Parameters:<a href="#{{ detail.name | lower() | replace(' ', '-') | e }}-parameters" class="headerlink" title="Permanent link"></a>
</h4>
<ul>
{% for parameter in detail.parameters %}
<li>
{% for attribute in parameter.attribute %}
<api__{{- attribute -}}></api__{{- attribute -}}>
{% endfor %}
<code>{{ parameter.type | e }}: {{ parameter.name }}</code>{% if parameter.description %} - {{ parameter.description }}{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% if detail.returns %}
<h4 id="{{ detail.name | lower() | replace(' ', '-') | e }}-returns">
Returns:<a href="#{{ detail.name | lower() | replace(' ', '-') | e }}-returns" class="headerlink" title="Permanent link"></a>
</h4>
<p class="api-detail__description">
{{ detail.returns }}
</p>
{% endif %}
{% if detail.throws %}
<h4 id="{{ detail.name | lower() | replace(' ', '-') | e }}-throws">
Throws:<a href="#{{ detail.name | lower() | replace(' ', '-') | e }}-throws" class="headerlink" title="Permanent link"></a>
</h4>
<ul>
{% for throw in detail.throws %}
<li>
<code>{{ throw.name }}</code>{% if throw.description %} - {{ throw.description }}{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% if seealso %}
<h4 id="{{ detail.name | lower() | replace(' ', '-') | e }}-see-also">
See also:<a href="#{{ detail.name | lower() | replace(' ', '-') | e }}-see-also" class="headerlink" title="Permanent link"></a>
</h4>
<ul>
{% for see in detail.seealso %}
<li>
<a href="{{ see.link }}"><code>{{ see.name }}</code></a>
</li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endfor %}
{%- endmacro %}
@@ -0,0 +1,61 @@
{% if page.meta.constructors %}
## Constructor Details
{% for constructor in page.meta.constructors %}
<div class="api-detail" markdown>
{% set name = namespace(text = constructor.name ~ "(") %}
{% for parameter in constructor.parameters %}
{% set name.text = name.text ~ parameter.type %}
{% if loop.revindex > 1 %}{% set name.text = name.text ~ ", " %}{% endif %}
{% endfor %}
{% set name.text = name.text ~ ")" %}
{% if constructor.deprecated %}
{% set name.text = "<api__deprecated></api__deprecated> " ~ name.text %}
{% endif %}
{% for attribute in constructor.attributes %}
{% set name.text = "<api__{{- attribute -}}></api__{{- attribute -}}> " ~ name.text %}
{% endfor %}
### {{ name.text | e }} { #{{ constructor.name | lower() | replace(' ', '-') | e }} }
{% if constructor.deprecated %}
/// deprecated | Deprecation Warning
{{ constructor.deprecated }}
///
{% endif %}
{% if constructor.description %}
<p class="api-detail__description">
{{ constructor.description }}
</p>
{% endif %}
{% if constructor.parameters %}
#### Parameters: { #{{ constructor.name | lower() | replace(' ', '-') | e }}-parameters }
{% for parameter in constructor.parameters %}
- {% for attribute in parameter.attributes %}<api__{{- attribute -}}></api__{{- attribute -}}>{% endfor %} `{{ parameter.type | e}}: {{ parameter.name }}`{% if parameter.description %} - {{ parameter.description }}{% endif %}
{% endfor %}
{% endif %}
{% if constructor.seealso %}
#### See also: { #{{ constructor.name | lower() | replace(' ', '-') | e }}-see-also }
<div class="api-detail__description" markdown>
{% for see in constructor.seealso %}
- [`{{ see.name }}`]({{ see.link }})
{% endfor %}
</div>
{% endif %}
</div>
{% endfor %}
{% endif %}
@@ -0,0 +1,14 @@
{% if page.meta.constructors %}
## Constructor Summary
| Constructor | Description |
|-------------|-------------|{% for constructor in page.meta.constructors %}
| [`{{ constructor.name }}({% for parameter in constructor.parameters -%}
{{ parameter.type }}{% if loop.revindex > 1 %}, {% endif %}
{%- endfor %})`](#{{ constructor.name | lower() }}) | {% if constructor.deprecated -%}
<api__deprecated></api__deprecated> {{ constructor.deprecated.split("\n")[0] }}
{%- elif constructor.description -%}
{{ constructor.description.split("\n")[0] }}
{%- endif %} |
{%- endfor %}
{% endif %}
+32
View File
@@ -0,0 +1,32 @@
{% if page.meta.enums %}
## Enum Constant Details
{% for enum in page.meta.enums %}
<div class="api-detail" markdown>
{% set modifiers = namespace(text = "<api__static></api__static> <api__final></api__final> ") %}
{% if enum.deprecated %}
{% set modifiers.text = "<api__deprecated></api__deprecated> " ~ modifiers.text %}
{% endif %}
### {{ modifiers.text }} `{{ enum.name | upper() }}` { #{{ enum.name | lower() | replace(' ', '-') | e }} }
{% if enum.deprecated %}
/// deprecated | Deprecation Warning
{{ enum.deprecated }}
///
{% endif %}
{% if enum.description %}
<div class="api-detail__description" markdown>
{{ enum.description }}
</div>
{% endif %}
</div>
{% endfor %}
{% endif %}
@@ -0,0 +1,8 @@
{% if page.meta.enums %}
## Enum Constant Summary
| Enum | Description |
|------|-------------|{% for enum in page.meta.enums %}
| [`{{ enum.name | upper() }}`](#{{ enum.name | lower() }}) | {{ enum.description.split("\n")[0] }} |
{%- endfor %}
{% endif %}
+3
View File
@@ -0,0 +1,3 @@
{% macro render_name(method) -%}
{{ method.name }}({% for parameter in method.parameters %}{{ parameter.type }}{% if loop.revindex > 1 %}, {% endif %}{% endfor %})
{%- endmacro %}
+109
View File
@@ -0,0 +1,109 @@
{% if page.meta.methods %}
## Method Details
{% for method in page.meta.methods %}
<div class="api-detail" markdown>
{% set modifiers = namespace(text = "") %}
{% if method.deprecated %}
{% set modifiers.text = "<api__deprecated></api__deprecated> " %}
{% endif %}
{% for attribute in method.attributes %}
{% set modifiers.text = modifiers.text ~ "<api__" ~ attribute ~ "></api__" ~ attribute ~ ">" %}
{% if loop.revindex > 0 %}{% set modifiers.text = modifiers.text ~ " " %}{% endif %}
{% endfor %}
{% if modifiers.text %}{% set modifiers.text = modifiers.text ~ " " %}{% endif %}
{% set method_type = "" %}
{% if method.type %}
{% if method.type.link %}
{% set method_type = '[' ~ method.type.name|default('void')|e ~ '](' ~ method.type.link ~ '){ .api-type__' ~ method.type.type|default('primitive') ~ '} ' %}
{% else %}
{% set method_type = '<span class="api-type__' ~ method.type.type|default('primitive') ~ '">' ~ method.type.name|default('void')|e ~ '</span> ' %}
{% endif %}
{% elif page.meta.defaults and page.meta.defaults.type %}
{% if page.meta.defaults.type.link %}
{% set method_type = '[' ~ page.meta.defaults.type.name|default('void')|e ~ '](' ~ page.meta.defaults.type.link ~ '){ .api-type__' ~ page.meta.defaults.type.type|default('primitive') ~ '} ' %}
{% else %}
{% set method_type = '<span class="api-type__' ~ page.meta.defaults.type.type|default('primitive') ~ '">' ~ page.meta.defaults.type.name|default('void')|e ~ '</span> ' %}
{% endif %}
{% else %}
{% set method_type = '<span class="api-type__primitive">void</span> ' %}
{% endif %}
{% set name = namespace(text = method.name ~ "(") %}
{% for parameter in method.parameters %}
{% set name.text = name.text ~ parameter.type %}
{% if loop.revindex > 1 %}{% set name.text = name.text ~ ", " %}{% endif %}
{% endfor %}
{% set name.text = name.text ~ ")" %}
### {{ modifiers.text }} {{ method_type }} `{{ name.text }}` { #{{ method.name | lower() | replace(' ', '-') | e }} }
{% if method.deprecated %}
/// deprecated | Deprecation Warning
{{ method.deprecated }}
///
{% endif %}
{% if method.description %}
<div class="api-detail__description" markdown>
{{ method.description }}
</div>
{% endif %}
{% if method.parameters %}
#### Parameters: { #{{ method.name | lower() | replace(' ', '-') | e }}-parameters }
<div class="api-detail__description" markdown>
{% for parameter in method.parameters %}
- {% for attribute in parameter.attributes %}<api__{{- attribute -}}></api__{{- attribute -}}>{% endfor %} `{{ parameter.type | e}}: {{ parameter.name }}`{% if parameter.description %} - {{ parameter.description }}{% endif %}
{% endfor %}
</div>
{% endif %}
{% if method.returns %}
#### Returns: { #{{ method.name | lower() | replace(' ', '-') | e }}-returns }
<div class="api-detail__description" markdown>
{{ method.returns }}
</div>
{% endif %}
{% if method.throws %}
#### Throws: { #{{ method.name | lower() | replace(' ', '-') | e }}-throws }
<div class="api-detail__description" markdown>
{% for throw in method.throws %}
- `{{ throw.name }}`{% if throw.description %} - {{ throw.description }}{% endif %}
{% endfor %}
</div>
{% endif %}
{% if method.seealso %}
#### See also: { #{{ method.name | lower() | replace(' ', '-') | e }}-see-also }
<div class="api-detail__description" markdown>
{% for see in method.seealso %}
- [`{{ see.name }}`]({{ see.link }})
{% endfor %}
</div>
{% endif %}
</div>
{% endfor %}
{% endif %}
@@ -0,0 +1,31 @@
{% if page.meta.methods or page.meta.inherits %}
## Method Summary
{% if page.meta.methods %}
| Modifier and Type | Method | Description |
|-------------------|--------|-------------|{% for method in page.meta.methods %}
| `{% if method.attributes -%}
{% for attribute in method.attributes -%}
{{ attribute }}{% if loop.revindex > 0 %} {% endif %}
{%- endfor %} {%- endif %}{% if method.type and method.type.name -%}
{{ method.type.name }}
{%- elif page.meta.defaults and page.meta.defaults.type and page.meta.defaults.type.name -%}
{{ page.meta.defaults.type.name }}
{%- else -%}void{%- endif %}` | [`{{ method.name }}({% for parameter in method.parameters -%}
{{ parameter.type }}{% if loop.revindex > 1 %}, {% endif %}
{%- endfor %})`](#{{ method.name | lower() }}) | {% if method.deprecated -%}
<api__deprecated></api__deprecated> {{ method.deprecated.split("\n")[0] }}
{%- elif method.description -%}
{{ method.description.split("\n")[0] }}
{%- endif %} |
{%- endfor %}
{% endif %}
{% if page.meta.inherits %}
{% for classpath, inherit in page.meta.inherits.items() %}
| Methods inherited from [`{{ classpath }}`]({{ inherit.link }}) |
|----------------------------------------------------------------|
| {% for entry in inherit.list -%}[`{{ entry.name | e }}`]({{ inherit.link }}#{{ entry.link | lower() | replace(' ', '-') | e }}){% if loop.revindex > 1 %}, {% endif %}{% endfor %} |
{% endfor %}
{% endif %}
{% endif %}
@@ -0,0 +1,11 @@
{% if page.meta.classes %}
## Nested Class Summary
| Modifier and Type | Class | Description |
|-------------------|-------|-------------|{% for class in page.meta.classes %}
| `{% if class.attributes -%}
{% for attribute in class.attributes -%}
{{ attribute }}{% if loop.revindex > 1 %} {% endif %}
{%- endfor %} {%- endif %}{{ class.type }}` | [`{{ class.name }}`]({{ class.link }}) | {{ class.description.split("\n")[0] }} |
{%- endfor %}
{% endif %}
+1 -3
View File
@@ -1,7 +1,5 @@
{% extends "base.html" %}
{% block announce %}
<span class="twemoji">{% include ".icons/material/party-popper.svg" %}</span>
Celebrating <a href="https://andre601.ch/blog/2024/12-13-history-of-asl/" target="_blank" rel="nofollow"><b>25,000 downloads!</b></a> Thank you!
<span class="twemoji">{% include ".icons/material/party-popper.svg" %}</span>
<span data-md-component="download-countdown"></span>
{% endblock %}
+1 -1
View File
@@ -1,6 +1,6 @@
[discord]: https://discord.gg/MyPTjpsgGH
[placeholderapi]: https://hangar.papermc.io/HelpChat/PlaceholderAPI
[placeholderapi]: https://modrinth.com/plugin/PlaceholderAPI
[viaversion]: https://hangar.papermc.io/ViaVersion/ViaVersion
[papiproxybridge]: https://hangar.papermc.io/William278/PAPIProxyBridge
[maintenance]: https://hangar.papermc.io/kennytv/Maintenance
@@ -16,8 +16,7 @@ The BanPlugins Addon is available in downloads of AdvancedServerList on [:simple
**Supported Platforms:**
- :fontawesome-solid-paper-plane: Paper
- :simple-spigotmc: BungeeCord
<!-- icon:paper --> <!-- icon:bungee -->
| Placeholder | Description |
|---------------------------------|------------------------------------------------------------------------------------------|
@@ -57,9 +56,7 @@ motd:
**Supported Platforms:**
- :fontawesome-solid-paper-plane: Paper
- :simple-spigotmc: BungeeCord
- :fontawesome-solid-paper-plane: Velocity
<!-- icon:all -->
| Placeholder | Description |
|---------------------------------|------------------------------------------------------------------------------------------|
@@ -95,9 +92,7 @@ motd:
**Supported Platforms:**
- :fontawesome-solid-paper-plane: Paper
- :simple-spigotmc: BungeeCord
- :fontawesome-solid-paper-plane: Velocity
<!-- icon:all -->
| Placeholder | Description |
|------------------------------|------------------------------------------------------------------------------------------|
+39
View File
@@ -0,0 +1,39 @@
---
icon: octicons/checklist-24
---
# LuckPerms Addon
The LuckPerms addon provides placeholders to return values from LuckPerms for a specific player.
This can be a players primary group, whether they have a specific group or whether they have a specific permission.
/// note
The Userdata retrieval is done asynchronously by LuckPerms.
However, this addon forces the thread for this to be blocked, which can cause lag, if done on the main thread.
This was done due to possible downsides and issues with trying to keep it async and block-free.
To mitigate possible lag is the retrieved user data cached for 1 minute before being discarded.
///
## Placeholders
**Supported Platforms:**
<!-- icon:all -->
| Placeholder | Description |
|-------------------------------------------|-----------------------------------------------------------------------------------------------------------------|
| `${luckperms hasGroup <group>}` | Returns whether the player has the specified group or not. |
| `${luckperms hasPermission <permission>}` | Returns whether the player has the specified permission.<br/>No permission or one set to `false` returns false. |
| `${luckperms primaryGroup}` | Gives the name of the primary group the player has. |
/// details | Example
type: example
```yaml title="is_admin.yml
priority: 1
condition: "${luckperms hasGroup admin}"
motd:
- "<yellow>Welcome Admin!"
```
///
+16 -2
View File
@@ -13,16 +13,30 @@ These Addons are made by Andre601 and available together with the plugin downloa
<div class="grid cards" markdown>
- [**BanPlugins**](banplugins.md)
- [**BanPlugins**](first-party/banplugins.md)
----
Addon for different Punishment plugins such as AdvancedBan, that add new placeholders to use in AdvancedServerList.
- [**LuckPerms**](first-party/luckperms.md)
----
Addon for adding LuckPerms-related placeholders to use in conditions.
</div>
## Third-party
Third-party addons made by other people not working on AdvancedServerList.
*Currently no entries*
<div class="grid cards" markdown>
- [**SayanVanish**](third-party/sayanvanish.md)
----
SayanVanish adds additional placeholders to the Velocity version of AdvancedServerList to use.
</div>
+43
View File
@@ -0,0 +1,43 @@
---
icon: octicons/eye-closed-24
---
# SayanVanish
The plugin [SayanVanish]{ target="_blank" rel="nofollow" } is hooking into AdvancedServerList to provide placeholders of its own to be used in conditions and text options.
## Placeholders
/// tab | :fontawesome-solid-paper-plane: Paper
[:octicons-file-code-24: Source][source-paper]
| Placeholder | Description |
|----------------------------------|--------------------------------------------------------------------------------------------|
| `${sayanvanish vanished}` | Returns true or false based on if the player is vanished. |
| `${sayanvanish level}` | Returns the vanish level of the player. |
| `${sayanvanish count}` | Returns the number of online players that are vanished. |
| `${sayanvanish online_here}` | Returns the number of online players on the same server that aren't vanished. |
| `${sayanvanish online_total}` | Returns the number of online players on the entire network that aren't vanished.^[1](#n1)^ |
| `${sayanvanish online_<server>}` | Returns the number of online players on `<server>` that aren't vanished.^[1](#n1)^ |
| `${sayanvanish vanish_prefix}` | Returns the prefix for the player while they are vanished or an empty String. |
| `${sayanvanish vanish_suffix}` | Returns the suffix for the player while they are vanished or an empty String. |
<small>^1^{ #n1 } Only works when Proxy mode is enabled and will otherwise return `PROXY_MODE IS NOT ENABLED!`</small>
///
/// tab | :simple-velocity: Velocity
[:octicons-file-code-24: Source][source-velocity]
| Placeholder | Description |
|----------------------------------|----------------------------------------------------------------------------------|
| `${sayanvanish vanished}` | Returns true or false based on if the player is vanished. |
| `${sayanvanish level}` | Returns the vanish level of the player. |
| `${sayanvanish count}` | Returns the number of online players that are vanished. |
| `${sayanvanish online_total}` | Returns the number of online players on the entire network that aren't vanished. |
| `${sayanvanish online_<server>}` | Returns the number of online players on `<server>` that aren't vanished. |
| `${sayanvanish vanish_prefix}` | Returns the prefix for the player while they are vanished or an empty String. |
| `${sayanvanish vanish_suffix}` | Returns the suffix for the player while they are vanished or an empty String. |
///
[SayanVanish]: https://modrinth.com/plugin/sayanvanish
[source-paper]: https://github.com/Syrent/SayanVanish/blob/6e03b874464a7f07c0baba233c94cdfefab5d3e2/sayanvanish-bukkit/src/main/kotlin/org/sayandev/sayanvanish/bukkit/feature/features/hook/FeatureHookAdvancedServerList.kt
[source-velocity]: https://github.com/Syrent/SayanVanish/blob/6e03b874464a7f07c0baba233c94cdfefab5d3e2/sayanvanish-proxy/sayanvanish-proxy-velocity/src/main/kotlin/org/sayandev/sayanvanish/velocity/feature/features/hook/FeatureHookAdvancedServerList.kt
+183
View File
@@ -0,0 +1,183 @@
---
icon: octicons/plus-circle-24
---
AdvancedServerList allows you to add your own Placeholders to be useable through its `${identifier value}` format.
The benefit of this method over alternatives such as using PlaceholderAPI placeholders is availability and the fact that these placeholders can be used within the [`condition` option](../profiles/options/condition.md) of the plugin.
Adding your own placeholder is relatively easy and only requires the main API module to be used in your dependencies, unless you need platform-specific values (Explained later on).
## Create your PlaceholderProvider class
AdvancedServerList's placeholder system uses a dedicated [`PlaceholderProvider`](reference/api/ch.andre601.advancedserverlist.api/placeholderprovider.md) class where the placeholder gets defined and its values parsed.
In order to add your own, you'll need to first create a new class and make it extend the `PlaceholderProvider` class:
```java title="MyPlaceholder.java"
public class MyPlaceholder extends PlaceholderProvider {
}
```
Your IDE should now report issues, namely a missing method to override and a missing Constructor to match the extended class' one. Your IDE should allow you to easily implement those, if not, then manually add the constructor with a `String` argument and the `public String parsePlaceholder` method with `String`, [`GenericPlayer`](reference/api/ch.andre601.advancedserverlist.api/objects/genericplayer.md) and [`GenericServer`](reference/api/ch.andre601.advancedserverlist.api/objects/genericserver.md) arguments:
```java { title="MyPlaceholder.java" .annotate }
public class MyPlaceholder extends PlaceholderProvider {
public MyPlaceholder(String identifier) {
super(identifier); // (1)
}
@Override
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server) {
return null;
}
}
```
1. It is recommended to replace the Constructor with a No-Args Constructor and set the placeholder name directly in the `super` call:
```java
public MyPlaceholder() {
super("myplaceholder");
}
```
## Parsing placeholders
To now parse your placeholders, all you need to do is edit the `parsePlaceholder` method.
AdvancedServerList calls this method whenever it finds a matching identifier of a PlaceholderProvider, with the `String` argument being the actual value after the identifier and the `GenericPlayer` and `GenericServer` arguments holding the Player and server values respectively.
As an example, assuming you simply want to return `Hello` for the placeholder `${myplaceholder sayHello}`, this would be the code for it:
```java { title="MyPlaceholder.java" .annotate }
public class MyPlaceholder extends PlaceholderProvider {
public MyPlaceholder() {
super("myplaceholder");
}
@Override
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server) {
if (placeholder.equals("sayHello")) {
return "Hello";
}
return null; // (1)
}
}
```
1. Returning `null` tells AdvancedServerList that the placeholder was invalid, making it return the placeholder unchanged, while also reporting an error, if used in `condition`.
## Registering the Placeholder
The final step now would be to register your placeholder for AdvancedServerList to use.
To do this, obtain an instance of the [`AdvancedServerListAPI`](reference/api/ch.andre601.advancedserverlist.api/advancedserverlistapi.md) through its static `get()` method and then call `addPlaceholderProvider(PlaceholderProvider)` through it:
/// tab | :fontawesome-solid-paper-plane: Paper Example
```java title="MyPlugin.java"
public class MyPlugin extends JavaPlugin {
@Override
public void onEnable() {
AdvancedServerListAPI api = AdvancedServerListAPI.get();
api.addPlaceholderProvider(new MyPlaceholder());
getLogger().info("Hooked into AdvancedServerList!");
}
}
```
///
/// tab | :octicons-git-merge-24: BungeeCord Example
```java title="MyPlugin.java"
public class MyPlugin extends Plugin {
@Override
public void onEnable() {
AdvancedServerListAPI api = AdvancedServerListAPI.get();
api.addPlaceholderProvider(new MyPlaceholder());
getLogger().info("Hooked into AdvancedServerList!");
}
}
```
///
/// tab | :simple-velocity: Velocity Example
```java title="MyPlugin.java"
@Plugin(
id = "myplugin",
version = "1.0.0",
authors = {"You?"},
dependencies = {
@Dependency(id = "advancedserverlist")
}
)
public class MyPlugin {
private final ProxyServer proxy;
private final Logger logger;
@Inject
public MyPlugin(ProxyServer proxy, Logger logger) {
this.proxy = proxy;
this.logger = logger;
}
@Subscribe
public void onProxyInitialization(ProxyInitializeEvent event) {
AdvancedServerListAPI api = AdvancedServerListAPI.get();
api.addPlaceholderProvider(new MyPlaceholder());
logger.info("Hooked into AdvancedServerList!");
}
}
```
///
## You're done
Your Placeholders should now be registered successfully if they check the following:
- The provided identifier is not `null` nor empty.
- The provided identifier does not contain spaces.
- The provided identifier is not already registered in the API.
## Note on `GenericPlayer` and `GenericServer`
The different platform APIs of AdvancedServerList provide extended versions of the `GenericPlayer` and `GenericServer` instances used, providing additional methods otherwise not available.
Should you want to use these specific instances will you need to add the platform API as an additional dependency to your project and then cast the `GenericPlayer` and/or `GenericServer` instance to the platform specific version.
Below is an example using the `platform-bukkit` API (This assumes your plugin is one running on Paper):
```java { title="MyPlaceholder.java" .annotate }
public class MyPlaceholder extends PlaceholderProvider {
public MyPlaceholder() {
super("myplaceholder");
}
@Override
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server) {
if (!(player instanceof BukkitPlayer bukkitPlayer)) {
return null; // (1)
}
return switch (placeholder) {
case "playedBefore" -> String.valueOf(bukkitPlayer.hasPlayedBefore());
case "whitelisted" -> String.valueOf(bukkitPlayer.isWhitelisted());
case "banned" -> String.valueOf(bukkitPlayer.isBanned());
default -> null;
};
}
}
```
1. Depending on the Java version you use will you need to alter the code:
```java
if (!(player instanceof BukkitPlayer)) {
return null;
}
// You need to cast it separately.
BukkitPlayer bukkitPlayer = (BukkitPlayer) player;
// ...use bukkitPlayer here.
```
+14 -14
View File
@@ -14,7 +14,7 @@ They are ordered newest to oldest.
- New setter method added to [`ProfileEntry.Builder`][profileentry.builder] class:
- [`hidePlayersHoverEnabled(NullBool)`][hidePlayersHoverEnabled]
[hidePlayersHoverEnabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#hideplayershoverenabled(nullbool)
[hidePlayersHoverEnabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#hideplayershoverenabled
## v4.0.0 { #v4.0.0 }
@@ -30,8 +30,8 @@ They are ordered newest to oldest.
- [`onlinePlayersCount(String)`][onlinePlayersCount]
- [`onlinePlayersEnabled(NullBool)`][onlinePlayersEnabled]
[onlinePlayersCount]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#onlineplayerscount(string)
[onlinePlayersEnabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#onlineplayersenabled(nullbool)
[onlinePlayersCount]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#onlineplayerscount
[onlinePlayersEnabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#onlineplayersenabled
## v3.3.0 { #v3.3.0 }
@@ -62,15 +62,15 @@ They are ordered newest to oldest.
- [`playerCountText(String)`][playercounttext]
- [`players(List<String>)`][players]
[extraplayerscount]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#extraplayerscount(string)
[extraplayersenabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#extraplayersenabled(nullbool)
[favicon]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#favicon(string)
[hideplayersenabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#hideplayersenabled(nullbool)
[maxplayerscount]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#maxplayerscount(string)
[maxplayersenabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#maxplayersenabled(nullbool)
[motd]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#motd(list<string>)
[playercounttext]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#playercounttext(string)
[players]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#players(list<string>)
[extraplayerscount]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#extraplayerscount
[extraplayersenabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#extraplayersenabled
[favicon]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#favicon
[hideplayersenabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#hideplayersenabled
[maxplayerscount]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#maxplayerscount
[maxplayersenabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#maxplayersenabled
[motd]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#motd
[playercounttext]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#playercounttext
[players]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md#players
### :octicons-pencil-24:{ .changelog-changed title="Changed" } Changed { #v3.2.0-changed }
@@ -137,8 +137,8 @@ They are ordered newest to oldest.
- `setExtraPlayerEnabled(Integer)` in `ProfileEntry.Builder` - Typo in method name.
[profileentry]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/index.md
[maxplayersenabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/index.md#maxplayersenabled()
[maxplayerscount]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/index.md#maxplayerscount()
[maxplayersenabled]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/index.md#maxplayersenabled
[maxplayerscount]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/index.md#maxplayerscount
[profileentry.builder]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md
+9 -161
View File
@@ -84,127 +84,21 @@ dependencies {
```
///
## Adding your own Placeholders
## Tutorials
<!-- admo:info This requires the main api module -->
Dedicated pages to explain aspects of the API are available:
The API allows you to add your own placeholders which would be available through the `${<identifier> <values>}` placeholder Pattern in a Server List profile.
To add your own placeholders, follow these steps:
### 1. Create a Placeholder class
You should first create a new class and make it extend the abstract [`PlaceholderProvider`][placeholderprovider]. This class includes a constructor and a method you need to add, so do that:
```java { .annotate title="Creating PlaceholderProvider class" }
public class MyPlaceholders extends PlaceholderProvider {
public MyPlaceholders(String identifier) {
super(identifier); // (1)
}
@Override
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server) {
return null;
}
}
```
1. It is recommended to replace this constructor with a no-args Constructor and have the placeholder name set in the `super` directly:
```java
public MyPlaceholders() {
super("myplaceholders");
}
```
The [`parsePlaceholder(String, GenericPlayer, GenericServer)`][parseplaceholder] method is used by AdvancedServerList to replace a matching placeholder with a value.
What you return is completely up to you. Just keep in mind that returning `null` will be treated as an invalid placeholder by AdvancedServerList, resulting in the placeholder being returned as-is without any changes.
[placeholderprovider]: reference/api/ch.andre601.advancedserverlist.api/placeholderprovider.md
[parseplaceholder]: reference/api/ch.andre601.advancedserverlist.api/placeholderprovider.md#parseplaceholder(string,-genericplayer,-genericserver)
/// details | Example of final class
type: example
```java title="MyPlaceholders.java"
public class MyPlaceholders extends PlaceholderProvider {
public MyPlaceholders() {
super("myplaceholders");
}
@Override
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server) {
if(placeholder.equalsIgnoreCase("hello"))
return "Hello " + player.getName();
return null;
}
}
```
/// html | div.grid.cards
- [:octicons-plus-circle-24: Adding Placeholders](adding-placeholders.md)
- [:octicons-broadcast-24: Listening for Events](listening-for-events.md)
///
### 2. Register the Placeholders
Next step should be to register the placeholder. To achieve this, first obtain an instance of the [`AdvancedServerListAPI`][advancedserverlistapi] by using the static [`get()`][get] method. After that call [`addPlaceholderProvider(PlaceholderProvider placeholderProvider)`][addplaceholderprovider].
Your code may look similar to this:
```java title="Registering PlaceholderProvider class"
public class MyPlugin extends JavaPlugin {
@Override
public void onEnable() {
AdvancedServerListAPI api = AdvancedServerListAPI.get();
api.addPlaceholderProvider(new MyPlaceholders());
}
}
```
[advancedserverlistapi]: reference/api/ch.andre601.advancedserverlist.api/advancedserverlistapi.md
[get]: reference/api/ch.andre601.advancedserverlist.api/advancedserverlistapi.md#get()
[addplaceholderprovider]: reference/api/ch.andre601.advancedserverlist.api/advancedserverlistapi.md#addplaceholderprovider(placeholderprovider)
This should register your PlaceholderExpansion as long as it is valid, meaning that the identifier...
- ...is not `null`
- ...is not containing spaces
- ...is not using a name already registered in the API
### 3. Declare AdvancedServerList as (soft)depend
## Declare AdvancedServerList as (soft)depend
The final thing you should make sure is to define AdvancedServerList as a depend or soft-depend for your plugin, to make sure it loads after AdvancedServerList.
Below are example setups for Spigot, Paper, BungeeCord and Velocity:
/// tab | :simple-spigotmc: Spigot
//// tab | Softdepend
```yaml title="plugin.yml"
name: "MyPlugin"
author: "author"
version: "1.0.0"
main: "com.example.plugin.ExamplePlugin"
softdepend:
- AdvancedServerList
```
////
//// tab | Depend
```yaml title="plugin.yml"
name: "MyPlugin"
author: "author"
version: "1.0.0"
main: "com.example.plugin.ExamplePlugin"
depend:
- AdvancedServerList
```
////
///
/// tab | :fontawesome-solid-paper-plane: Paper
//// tab | Softdepend
```yaml title="paper-plugin.yml"
@@ -239,7 +133,7 @@ dependencies:
////
///
/// tab | :octicons-git-merge-24: BungeeCord
/// tab | :simple-spigotmc: BungeeCord
//// tab | Softdepend
```yaml title="bungee.yml"
name: "MyPlugin"
@@ -267,7 +161,7 @@ depends:
////
///
/// tab | :octicons-git-merge-24: Velocity
/// tab | :simple-velocity: Velocity
//// tab | Softdepend (File)
```json title="velocity-plugin.json"
{
@@ -355,50 +249,4 @@ public class MyPlugin {
### 4. You're done!
Your plugin should now hook into AdvancedServerList and register its own custom placeholders to use.
## Listening for events
<!-- admo:info These events require the platform-specific APIs -->
AdvancedServerList provides Events that your plugin can listen for specific situations.
### PreServerListSetEvent
This event is called **before** AdvancedServerList starts handling the PingEvent of the Server/Proxy.
It allows you to get and alter the [ProfileEntry][profileentry] used in the PingEvent, or even outright cancel the event itself.
The event offers the following methods:
- [`getEntry()`][getentry] - Gets the currently set [`ProfileEntry`][profileentry].
- [`setEntry(ProfileEntry)`][setentry] - Sets a new [`ProfileEntry`][profileentry] to use.
- [`isCancelled()`][iscancelled] - Gets the current cancelled state of the event.
- [`setCancelled(boolean)`][setcancelled] - Sets the current cancelled state.
Please check the server/proxy's documentation on how to listen for events with your plugin.
[getentry]: reference/api/ch.andre601.advancedserverlist.api/events/genericserverlistevent.md#getentry()
[setentry]: reference/api/ch.andre601.advancedserverlist.api/events/genericserverlistevent.md#setentry(profileentry)
[iscancelled]: reference/api/ch.andre601.advancedserverlist.api/events/genericserverlistevent.md#iscancelled()
[setcancelled]: reference/api/ch.andre601.advancedserverlist.api/events/genericserverlistevent.md#setcancelled(boolean)
### PostServerListSetEvent
This event is called **after** AdvancedServerList has completed its handling of the Server's/Proxy's PingEvent.
It provides a method to retrieve the [ProfileEntry][profileentry] used during the PingEvent.
Do note that this ProfileEntry is **not** a representation of what is actually displayed in the server list, but rather what has been used by AdvancedServerList. It may also be `null` should the event handling get cancelled for any reason.
[profileentry]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/index.md
### ProfileEntry
The ProfileEntry record is the core class used within the `PreServerListSetEvent`. It sets what values (MOTD, Favicon, Players, etc.) should be displayed.
Note that the record is - by nature - immutable and that the [`Builder`][builder] class should be used to create a new ProfileEntry instance to use.
A convenience method ([`builder()`][builder()]) exists to create a `Builder` instance from an existing `ProfileEntry` in case you only want to alter certain options while everything else should remain unchanged.
This allows you to customize the Server list using your plugin. Just keep in mind that other plugins may also do the same, overriding your changes.
[builder]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md
[builder()]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/index.md#builder()
Your plugin should now hook into AdvancedServerList and register its own custom placeholders to use.
+170
View File
@@ -0,0 +1,170 @@
---
icon: octicons/broadcast-24
---
AdvancedServerList provides some events that your plugin can listen to.
These events are structurally the same across all platforms, yet still require the usage of Platform-specific APIs due to how some events are registered and handled on some platforms.
The plugin currently offers the following events:
/// html | div.grid.cards
- [PreServerListSetEvent](#preserverlistsetevent)
- [PostServerListSetEvent](#postserverlistsetevent)
///
## PreServerListSetEvent
This event is executed before AdvancedServerList tries to modify the player list.
### Methods { #preserverlistsetevent-methods }
The following methods are available to use:
| Method | Description |
|-----------------------------------------|--------------------------------------------------------------------------------|
| [`getEntry()`][getentry] | Returns the [`ProfileEntry`][profileentry] that AdvancedServerList should use. |
| [`setEntry(ProfileEntry)`][setentry] | Sets the [`ProfileEntry`][profileentry] that should be used. |
| [`isCancelled()`][iscancelled] | Returns wether this event was cancelled or not. |
| [`setCancelled(boolean)`][setcancelled] | Sets the event's cancelled state. |
[setentry]: reference/api/ch.andre601.advancedserverlist.api/events/genericserverlistevent.md#setentry
[iscancelled]: reference/api/ch.andre601.advancedserverlist.api/events/genericserverlistevent.md#iscancelled
[setcancelled]: reference/api/ch.andre601.advancedserverlist.api/events/genericserverlistevent.md#setcancelled
### Notes { #preserverlistsetevent-notes }
- Providing `null` as the `ProfileEntry` for `setEntry(ProfileEntry)` is not allowed. Also, a copy of the provided `ProfileEntry` is made and used.
## PostServerListSetEvent
This event is executed after AdvancedServerList completed the server list modifications.
### Methods { #postserverlistsetevent-methods }
| Method | Description |
|--------------------------|--------------------------------------------------------------------------|
| [`getEntry()`][getentry] | Returns the [`ProfileEntry`][profileentry] that AdvancedServerList used. |
[getentry]: reference/api/ch.andre601.advancedserverlist.api/events/genericserverlistevent.md#getentry
### Notes { #postserverlistsetevent-notes }
- The returned `ProfileEntry` is not an actual representation of what is being displayed to the player and rather an instance of values the plugin received.
The returned value may also be `null` should the Ping Event be cancelled due to things such as an invalid Protocol or the [`PreServerListSetEvent`](#preserverlistsetevent) being cancelled.
## Registering the Events
/// tab | :fontawesome-solid-paper-plane: Paper
```java { title="MyPlugin.java" .annotate }
public class MyPlugin extends JavaPlugin {
@Override
public void onEnable() {
// (1)
Bukkit.getPluginManager().registerEvents(new ASLEventListener(), this);
}
}
```
1. `getServer()` from the `JavaPlugin` class can be used here too instead of the `Bukkit` singleton.
```java { title="ASLEventListener.java" }
public class ASLEventListener implements Listener {
@EventHandler
public void onPreServerListSet(PreServerListSetEvent event) {
System.out.println("PreServerListSetEvent fired!");
}
@EventHandler
public void onPostServerListSetEvent(PostServerListSetEvent event) {
System.out.println("PostServerListSetEvent fired!");
}
}
```
///
/// tab | :simple-spigotmc: BungeeCord
```java { title="MyPlugin.java" }
public class MyPlugin extends Plugin {
@Override
public void onEnable() {
getProxy().getPluginManager().registerListener(this, new ASLEventListener());
}
}
```
```java { title="ASLEventListener.java" }
public class ASLEventListener implements Listener {
@EventHandler
public void onPreServerListSet(PreServerListSetEvent event) {
System.out.println("PreServerListSetEvent fired!");
}
@EventHandler
public void onPostServerListSetEvent(PostServerListSetEvent event) {
System.out.println("PostServerListSetEvent fired!");
}
}
```
///
/// tab | :simple-velocity: Velocity
```java { title="MyPlugin.java" }
@Plugin(
id = "myplugin",
name = "MyPlugin",
version = "1.0.0",
authors = {"author"},
dependencies = {
@Dependency(
id = "advancedserverlist",
)
}
)
public class MyPlugin {
private final ProxyServer proxy;
@Inject
public MyPlugin(ProxyServer server) {
this.proxy = proxy;
}
@Subscribe
public void init(ProxyInitializeEvent event) {
proxy.getEventManager().register(this, new ASLEventListener());
}
}
```
```java { title="ASLEventListener.java" }
public class ASLEventListener {
@Subscribe
public void onPreServerListSet(PreServerListSetEvent event) {
System.out.println("PreServerListSetEvent fired!");
}
@Subscribe
public void onPostServerListSetEvent(PostServerListSetEvent event) {
System.out.println("PostServerListSetEvent fired!");
}
}
```
///
## What is a ProfileEntry?
Both events provide and use a so called [`ProfileEntry`][profileentry].
The `ProfileEntry` is a record, representing the values of a [Server List Profile](../profiles/index.md), meaning all values you can set in the profile configuration (Except condition and priority) will be available as a `ProfileEntry`.
It is important to note, that the `ProfileEntry` can be a representation of the [`profiles` option](../profiles/index.md#profiles), the global options or a mixture of both.
If you want to create your own instance, you'll need to use the [`ProfileEntry.Builder` class](reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/builder.md) as the `ProfileEntry` is unmodifiable.
A convenience method in the form of [`builder()`][builder] exists to create a Builder instance of the ProfileEntry with its values already pre-set.
[profileentry]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/index.md
[builder]: reference/api/ch.andre601.advancedserverlist.api/profiles/profileentry/index.md#builder
@@ -1,10 +1,10 @@
---
template: api-doc.html
api: true
methods:
- name: 'get'
description: |
Retrieve the instance used of this API.<br>
Retrieve the instance used of this API.
If no instance has been made so far will a new one be created.
returns: 'Instance of this API.'
attributes:
@@ -14,38 +14,38 @@ methods:
type: 'object'
- name: 'addPlaceholderProvider'
description: |
Adds the provided <a href="./../placeholderprovider/"><code>PlaceholderProvider</code></a> to the list, if it passes the following checks:<br>
<ul>
<li>Provided PlaceholderProvider instance is not null.</li>
<li>The identifier is not null nor empty.</li>
<li>The identifier does not contain any spaces.</li>
<li>A PlaceholderProvider with the same identifier doesn't exist already.</li>
</ul>
Not passing any of the above checks results in a <a href="./../exceptions/invalidplaceholderproviderexception/"><code>InvalidPlaceholderProviderException</code></a> being thrown.
Adds the provided [`PlaceholderAPIProvider`](placeholderprovider.md) to the list, if it passes the following checks:
- Provided PlaceholderProvider instance is not null.
- The identifier is not null nor empty.
- The identifier does not contain any spaces.
- A PlaceholderProvider with the same identifier doesn't exist already.
Not passing any of the above checks results in a [`InvalidPlaceholderProviderException`](exceptions/invalidplaceholderproviderexception.md) being thrown.
parameters:
- name: 'placeholderProvider'
description: 'The <a href="./../placeholderprovider/"><code>PlaceholderProvider</code></a> to add.'
description: 'The [`PlaceholderProvider`](placeholderprovider.md) to add.'
type: PlaceholderProvider
attributes:
- notnull
throws:
- name: 'InvalidPlaceholderProviderException'
description: 'When the provided <a href="./../placeholderprovider/"><code>PlaceholderProvider</code> instance</a> is null, has a null or empty identifier, the identifier contains spaces, or another PlaceholderProvider with the same identifier is already in use.'
description: 'When the provided [`PlaceholderProvider`](placeholderprovider.md) is null, has a null or empty identifier, the identifier contains spaces, or another PlaceholderProvider with the same identifier is already in use.'
type:
name: 'void'
- name: 'retrievePlaceholderProvider'
description: 'Retrieves the <a href="./../placeholderprovider/"><code>PlaceholderProvider</code></a> associated with the provided identifier, or <code>null</code> should no such entry exist.'
description: 'Retrieves the [`PlaceholderProvider`](placeholderprovider.md) associated with the provided identifier, or `null` should no such entry exist.'
parameters:
- name: 'identifier'
description: 'The identifier to find a matching <a href="./../placeholderprovider/"><code>PlaceholderProvider</code></a> for.'
description: 'The identifier to find a matching [`PlaceholderProvider`](placeholderprovider.md) for.'
type: String
returns: 'Possibly-null <a href="./../placeholderprovider/"><code>PlaceholderProvider</code> instance</a>.'
returns: 'Possibly-null [`PlaceholderProvider` instance](placeholderprovider.md).'
attributes:
- nullable
type:
name: PlaceholderProvider
type: object
link: './../placeholderprovider'
link: 'placeholderprovider.md'
---
# <api__class></api__class> AdvancedServerListAPI
@@ -1,27 +1,27 @@
---
template: api-doc.html
api: true
methods:
- name: 'getEntry'
description: 'Gets the <a href="../../profiles/profileentry/"><code>ProfileEntry</code></a> currently set.'
description: 'Gets the [`ProfileEntry`](../profiles/profileentry/index.md) currently set.'
returns: The currently used ProfileEntry.
type:
name: ProfileEntry
type: object
link: '../../profiles/profileentry/'
link: '../profiles/profileentry/index.md'
- name: 'setEntry'
description: |-
Sets the new <a href="../../profiles/profileentry/"><code>ProfileEntry</code></a> to use.<br>
This may not be <code>null</code>.
Sets the new [`ProfileEntry`](../profiles/profileentry/index.md) to use.
This may not be `null`.
parameters:
- name: 'entry'
description: 'The new <a href="../../profiles/profileentry/"><code>ProfileEntry</code></a> to use.'
description: 'The new [`ProfileEntry`](../profiles/profileentry/index.md) to use.'
type: ProfileEntry
attributes:
- notnull
throws:
- name: 'IllegalArgumentException'
description: 'When the provided ProfileEntry is <code>null</code>'
description: 'When the provided ProfileEntry is `null`'
type:
name: 'void'
- name: 'isCancelled'
Loaded 100 of 200 files, more files were not shown because too many files have changed in this diff. Show more