Compare commits

...
244 Commits
Author SHA1 Message Date
Andre_601 d3673a79af Update README.md 2023-01-23 20:59:26 +01:00
Andre_601 29c6930ce1 Merge pull request #38 from Andre601/feature/add-api
Start with an API for custom placeholder support
2023-01-23 20:54:03 +01:00
Andre601 125e9c0918 Add missing files to git 2023-01-23 20:53:21 +01:00
Andre601 2ac6931f26 Remove unused files and add workflow 2023-01-23 20:52:37 +01:00
Andre601 e301ef38f0 Bump version + Docs 2023-01-23 20:36:28 +01:00
Andre_601 3f6ada4599 Create api_support.yml 2023-01-23 14:27:41 +01:00
Andre601 45d3f17faa Use a Constructor for PlaceholderProvider 2023-01-22 15:00:01 +01:00
Andre601 d8fca2ca03 Improve Javadocs and rename getUuid to getUUID 2023-01-21 00:14:06 +01:00
Andre601 730b2e3aa8 Get rid of <T> nonsense for GenericPlayer 2023-01-21 00:03:55 +01:00
Andre601 e4e3543b9b Check for null/empty before lowercasing identifier 2023-01-20 23:18:59 +01:00
Andre_601 a955109db5 Merge pull request #44 from Andre601/dependabot/maven/core/com.unascribed-flexver-java-1.1.0
Bump flexver-java from 1.0.2 to 1.1.0 in /core
2023-01-17 16:55:01 +01:00
dependabot[bot] d399c7df63 Bump flexver-java from 1.0.2 to 1.1.0 in /core
Bumps flexver-java from 1.0.2 to 1.1.0.

---
updated-dependencies:
- dependency-name: com.unascribed:flexver-java
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-17 15:11:31 +00:00
Andre601 973620925d fix wrong char position 2023-01-16 15:22:42 +01:00
Andre601 697e9e265d Update javadoc 2023-01-11 23:14:28 +01:00
Andre601 cabed6aa65 Update missing copyright headers 2023-01-11 23:10:00 +01:00
Andre601 b408658026 Apply fixes 2023-01-11 23:08:41 +01:00
Andre601 15f611445c Merge branch 'master' into feature/add-api
# Conflicts:
#	api/src/main/java/ch/andre601/advancedserverlist/api/exceptions/InvalidPlaceholderProviderException.java
#	bungeecord/src/main/java/ch/andre601/advancedserverlist/bungeecord/events/BungeeEventWrapper.java
#	core/src/main/java/ch/andre601/advancedserverlist/core/events/PingEventHandler.java
#	core/src/main/java/ch/andre601/advancedserverlist/core/interfaces/events/GenericEventWrapper.java
#	paper/src/main/java/ch/andre601/advancedserverlist/paper/events/PaperEventWrapper.java
#	paper/src/main/java/ch/andre601/advancedserverlist/paper/objects/PaperPlayer.java
#	paper/src/main/java/ch/andre601/advancedserverlist/paper/objects/PlayerPlaceholders.java
#	spigot/src/main/java/ch/andre601/advancedserverlist/spigot/events/ProtocolLibEventWrapper.java
#	spigot/src/main/java/ch/andre601/advancedserverlist/spigot/objects/SpigotPlayer.java
#	velocity/src/main/java/ch/andre601/advancedserverlist/velocity/events/VelocityEventWrapper.java
#	velocity/src/main/java/ch/andre601/advancedserverlist/velocity/objects/VelocityPlayer.java
2023-01-11 22:45:43 +01:00
Andre_601 3859f2970f Merge pull request #41 from Andre601/feature/uuid-usage
Improve player caching behaviour
2023-01-10 20:36:34 +01:00
Andre601 71079bf9d8 Bump version 2023-01-09 20:47:46 +01:00
Andre601 4fa7e92264 Make CachedPlayer its own class 2023-01-09 20:46:46 +01:00
Andre_601 828d907371 Merge branch 'master' into feature/uuid-usage 2023-01-09 20:37:36 +01:00
Andre601 986c36a334 tiny typo fix 2023-01-09 20:36:08 +01:00
Andre_601 8f9ae4a835 Merge pull request #43 from Andre601/fix/nullpointerexceptions
Fix some NullPointerExceptions
2023-01-09 20:32:01 +01:00
Andre601 0dccdaf7e4 Add a comment 2023-01-09 20:31:31 +01:00
Andre601 ec0dec8e7d Try to fix errors with OkHttpClient 2023-01-09 17:59:23 +01:00
Andre601 f399ed6886 Fix some NullPointerExceptions 2023-01-09 17:42:54 +01:00
Andre_601 f2737f2b19 Merge pull request #42 from Andre601/dependabot/maven/core/com.google.code.gson-gson-2.10.1
Bump gson from 2.10 to 2.10.1 in /core
2023-01-09 16:20:46 +01:00
dependabot[bot] 504e669ce7 Bump gson from 2.10 to 2.10.1 in /core
Bumps [gson](https://github.com/google/gson) from 2.10 to 2.10.1.
- [Release notes](https://github.com/google/gson/releases)
- [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/gson/compare/gson-parent-2.10...gson-parent-2.10.1)

---
updated-dependencies:
- dependency-name: com.google.code.gson:gson
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-09 15:16:41 +00:00
Andre601 0cae58c9b6 Update Copyright 2023-01-01 00:33:39 +01:00
Andre_601 a8d6fc48bf Merge branch 'master' into feature/add-api 2022-12-31 23:06:26 +01:00
Andre601 91dcb49e81 Update log messages 2022-12-31 16:40:57 +01:00
Andre601 c8a1224f53 Fix broken logic and make ${player uuid} always available 2022-12-31 16:21:26 +01:00
Andre601 5722700d20 Improve player caching behaviour 2022-12-31 01:25:57 +01:00
Andre_601 1effc2574a Update release.yml 2022-12-28 20:15:16 +01:00
Andre_601 eb7a7fa6c7 Delete prerelease.yml 2022-12-28 20:15:07 +01:00
Andre601 9f6bdea168 Add better Version check (+ tests) 2022-12-28 16:58:23 +01:00
Andre601 c964c942dc Implement latest changes from v1.10.0 2022-12-28 14:00:19 +01:00
Andre601 27c591d92c Merge branch 'master' into feature/add-api
# Conflicts:
#	core/src/main/java/ch/andre601/advancedserverlist/core/events/PingEventHandler.java
#	core/src/main/java/ch/andre601/advancedserverlist/core/profiles/ProfileManager.java
#	core/src/main/java/ch/andre601/advancedserverlist/core/profiles/ServerListProfile.java
2022-12-28 13:47:46 +01:00
Andre_601 d247d832e6 Update prerelease.yml 2022-12-27 22:02:32 +01:00
Andre_601 708037bb6b Update release.yml 2022-12-27 22:02:05 +01:00
Andre_601 7da88771b6 Update prerelease.yml 2022-12-27 21:27:01 +01:00
Andre_601 9ccfd4674c Add workflow for beta-releases 2022-12-27 21:26:16 +01:00
Andre_601 9054ebdb0c Merge pull request #40 from Andre601/feature/multi-profiles
Implement system for multiple profiles in profiles
2022-12-27 21:22:25 +01:00
Andre601 edfd77e891 Fix wrong hidePlayersEnabled call. 2022-12-27 21:19:01 +01:00
Andre601 94391a8aed Make booleans and int nullable + version bump 2022-12-27 21:16:23 +01:00
Andre601 5a1f343cc2 Update config.yml 2022-12-27 18:03:07 +01:00
Andre601 e718a5c766 Improve descriptions 2022-12-27 18:01:40 +01:00
Andre601 565644ccd5 Add profiles example 2022-12-27 17:51:07 +01:00
Andre601 9531af1fdd Don't use random on a single entry 2022-12-27 16:56:50 +01:00
Andre601 7c1d7aadbc Try a system with fallback to def profile 2022-12-27 16:46:02 +01:00
Andre601 f26f2ba269 Implement system for multiple profiles in profiles 2022-12-27 16:03:01 +01:00
Andre601 de2d43f01c fix methods and imports 2022-12-20 23:48:03 +01:00
Andre_601 3fde989eb5 Merge branch 'master' into feature/add-api 2022-12-20 23:44:28 +01:00
Andre_601 f5d2fd1534 Merge pull request #39 from Andre601/feature/multiple-motds
Add option to have multiple MOTDs.
2022-12-20 22:22:49 +01:00
Andre601 90537eb004 Bump Version for release 2022-12-20 22:22:14 +01:00
Andre601 ce6a9e3b0b Use a Builder class to make ServerListProfile immutable... I guess? 2022-12-18 21:32:35 +01:00
Andre601 4d29f9e45e Add option to have multiple MOTDs. 2022-12-18 17:45:38 +01:00
Andre601 c1fa8d0df1 Add maven-compiler-plugin + throws docs 2022-12-16 17:05:06 +01:00
Andre601 f0446fe8f4 Move API to own module 2022-12-16 16:55:21 +01:00
Andre601 5509fb4380 Add and improve javadoc comments 2022-12-11 23:46:26 +01:00
Andre601 d0f7bf9897 Start with an API for custom placeholder support 2022-12-11 23:26:54 +01:00
Andre601 bf49636d6a Update workflow 2022-12-09 22:27:44 +01:00
Andre601 4a6c9a55d4 Update required libraries 2022-12-09 22:26:44 +01:00
Andre601 a5bfc0b6e3 Smaller format correction + version bump 2022-12-09 22:23:24 +01:00
Andre_601 dff332a83a Merge pull request #37 from Andre601/dependabot/maven/spigot/net.kyori-adventure-platform-bukkit-4.2.0
Bump adventure-platform-bukkit from 4.1.2 to 4.2.0 in /spigot
2022-12-09 16:10:29 +01:00
dependabot[bot] 58a16baaab Bump adventure-platform-bukkit from 4.1.2 to 4.2.0 in /spigot
Bumps [adventure-platform-bukkit](https://github.com/KyoriPowered/adventure-platform) from 4.1.2 to 4.2.0.
- [Release notes](https://github.com/KyoriPowered/adventure-platform/releases)
- [Commits](https://github.com/KyoriPowered/adventure-platform/compare/v4.1.2...v4.2.0)

---
updated-dependencies:
- dependency-name: net.kyori:adventure-platform-bukkit
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-09 15:08:40 +00:00
Andre_601 c8acb4d90a Merge pull request #36 from Andre601/feature/unified-eventhandler
Create unified EventHandler
2022-12-08 21:56:48 +01:00
Andre_601 5575243a79 Merge pull request #35 from Andre601/dependabot/maven/core/com.google.code.gson-gson-2.10
Bump gson from 2.9.1 to 2.10 in /core
2022-12-08 16:43:13 +01:00
Andre601 4cd4aeb12a Move Favicon creation and logging to PingEventHandler 2022-12-08 16:42:23 +01:00
Andre601 5922b2235e Bump version 2022-12-07 21:19:23 +01:00
Andre601 2fcef08f5a Wrap up (no pun intended) remaining platform events 2022-12-07 16:28:39 +01:00
Andre601 30c28b07a4 Smaller changes 2022-12-06 21:32:16 +01:00
Andre601 08b4f1029c Make Protocol and ServerPing final 2022-12-06 21:27:09 +01:00
Andre601 f5051022aa Create unified EventHandler 2022-12-06 21:24:43 +01:00
dependabot[bot] fe51e78764 Bump gson from 2.9.1 to 2.10 in /core
Bumps [gson](https://github.com/google/gson) from 2.9.1 to 2.10.
- [Release notes](https://github.com/google/gson/releases)
- [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/gson/compare/gson-parent-2.9.1...gson-parent-2.10)

---
updated-dependencies:
- dependency-name: com.google.code.gson:gson
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-06 15:11:07 +00:00
Andre_601 5b2d9d910f Use env variables 2022-12-06 14:17:34 +01:00
Andre_601 6f06c47de7 Merge pull request #34 from Andre601/feature/update-checker
Add Update Checker
2022-12-06 14:03:25 +01:00
Andre601 ad3ba0c8b3 Update config description 2022-12-06 00:49:52 +01:00
Andre601 12d8d930dd Use a ScheduledExecutorService instead of Timer 2022-12-06 00:26:55 +01:00
Andre601 9a30a6b679 Add info line 2022-12-06 00:01:24 +01:00
Andre601 b82cf69d43 Simplify more. 2022-12-05 23:58:05 +01:00
Andre601 fb6d93b455 Init timer only when UpdateChecker is used. 2022-12-05 23:55:54 +01:00
Andre601 a1276da68f Fix exceptions 2022-12-05 23:13:21 +01:00
Andre601 25c053d495 Use timer class from Java 2022-12-05 22:24:19 +01:00
Andre601 39c1fb5452 Better version comparison. 2022-12-05 21:26:24 +01:00
Andre601 48edebc043 Add config option 2022-12-03 20:29:45 +01:00
Andre601 3a672378ff Add Update Checker 2022-12-03 20:23:21 +01:00
Andre_601 9db9f2be7f Merge pull request #33 from Andre601/dependabot/maven/core/net.kyori-adventure-api-4.12.0
Bump adventure-api from 4.11.0 to 4.12.0 in /core
2022-11-28 16:31:53 +01:00
Andre_601 2cf47e351b Merge pull request #32 from Andre601/dependabot/maven/core/net.kyori-adventure-text-minimessage-4.12.0
Bump adventure-text-minimessage from 4.11.0 to 4.12.0 in /core
2022-11-28 16:31:39 +01:00
Andre_601 36abdb83cd Merge pull request #31 from Andre601/dependabot/maven/core/net.kyori-adventure-text-serializer-legacy-4.12.0
Bump adventure-text-serializer-legacy from 4.11.0 to 4.12.0 in /core
2022-11-28 16:31:21 +01:00
dependabot[bot] ad950bf330 Bump adventure-api from 4.11.0 to 4.12.0 in /core
Bumps [adventure-api](https://github.com/KyoriPowered/adventure) from 4.11.0 to 4.12.0.
- [Release notes](https://github.com/KyoriPowered/adventure/releases)
- [Commits](https://github.com/KyoriPowered/adventure/compare/v4.11.0...v4.12.0)

---
updated-dependencies:
- dependency-name: net.kyori:adventure-api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-28 15:12:17 +00:00
dependabot[bot] 143753d309 Bump adventure-text-minimessage from 4.11.0 to 4.12.0 in /core
Bumps [adventure-text-minimessage](https://github.com/KyoriPowered/adventure) from 4.11.0 to 4.12.0.
- [Release notes](https://github.com/KyoriPowered/adventure/releases)
- [Commits](https://github.com/KyoriPowered/adventure/compare/v4.11.0...v4.12.0)

---
updated-dependencies:
- dependency-name: net.kyori:adventure-text-minimessage
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-28 15:12:10 +00:00
dependabot[bot] 0c37c21650 Bump adventure-text-serializer-legacy from 4.11.0 to 4.12.0 in /core
Bumps [adventure-text-serializer-legacy](https://github.com/KyoriPowered/adventure) from 4.11.0 to 4.12.0.
- [Release notes](https://github.com/KyoriPowered/adventure/releases)
- [Commits](https://github.com/KyoriPowered/adventure/compare/v4.11.0...v4.12.0)

---
updated-dependencies:
- dependency-name: net.kyori:adventure-text-serializer-legacy
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-28 15:12:04 +00:00
Andre_601 40a6b4f059 Merge pull request #30 from Andre601/dependabot/maven/core/com.github.ben-manes.caffeine-caffeine-3.1.2
Bump caffeine from 3.1.1 to 3.1.2 in /core
2022-11-24 16:24:36 +01:00
dependabot[bot] 965e87d71b Bump caffeine from 3.1.1 to 3.1.2 in /core
Bumps [caffeine](https://github.com/ben-manes/caffeine) from 3.1.1 to 3.1.2.
- [Release notes](https://github.com/ben-manes/caffeine/releases)
- [Commits](https://github.com/ben-manes/caffeine/compare/v3.1.1...v3.1.2)

---
updated-dependencies:
- dependency-name: com.github.ben-manes.caffeine:caffeine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-24 15:08:34 +00:00
Andre_601 202a22f33a Update README.md 2022-11-21 15:20:02 +01:00
Andre_601 a4b2a6c350 Update MODRINTH.md 2022-11-21 15:03:28 +01:00
Andre601 ba55f5299b Bump version 2022-11-07 22:29:04 +01:00
Andre_601 24fdf798cf Merge pull request #29 from Andre601/feature/disable-cache-option
Add option to disable Cache
2022-11-07 22:28:16 +01:00
Andre601 e208d181b7 Add option to disable Cache 2022-11-07 22:14:50 +01:00
Andre_601 6c15c7b259 Merge pull request #28 from Andre601/dependabot/maven/paper/org.apache.maven.plugins-maven-shade-plugin-3.4.1
Bump maven-shade-plugin from 3.4.0 to 3.4.1 in /paper
2022-10-27 17:55:21 +02:00
Andre_601 dcd76a43f5 Merge pull request #27 from Andre601/dependabot/maven/bungeecord/org.apache.maven.plugins-maven-shade-plugin-3.4.1
Bump maven-shade-plugin from 3.4.0 to 3.4.1 in /bungeecord
2022-10-27 17:55:10 +02:00
Andre_601 adc2060499 Merge pull request #26 from Andre601/dependabot/maven/velocity/org.apache.maven.plugins-maven-shade-plugin-3.4.1
Bump maven-shade-plugin from 3.4.0 to 3.4.1 in /velocity
2022-10-27 17:55:01 +02:00
Andre_601 4d6befc857 Merge pull request #25 from Andre601/dependabot/maven/core/org.apache.maven.plugins-maven-shade-plugin-3.4.1
Bump maven-shade-plugin from 3.4.0 to 3.4.1 in /core
2022-10-27 17:54:52 +02:00
dependabot[bot] 7cfb792471 Bump maven-shade-plugin from 3.4.0 to 3.4.1 in /paper
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.4.0 to 3.4.1.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.4.0...maven-shade-plugin-3.4.1)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-27 15:41:21 +00:00
dependabot[bot] d46245eefa Bump maven-shade-plugin from 3.4.0 to 3.4.1 in /bungeecord
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.4.0 to 3.4.1.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.4.0...maven-shade-plugin-3.4.1)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-27 15:41:17 +00:00
dependabot[bot] d5e0fd3d7a Bump maven-shade-plugin from 3.4.0 to 3.4.1 in /velocity
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.4.0 to 3.4.1.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.4.0...maven-shade-plugin-3.4.1)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-27 15:41:11 +00:00
dependabot[bot] 496e246b9e Bump maven-shade-plugin from 3.4.0 to 3.4.1 in /core
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.4.0 to 3.4.1.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.4.0...maven-shade-plugin-3.4.1)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-27 15:41:11 +00:00
Andre_601 e263a45f1c width and not height 2022-10-05 17:40:40 +02:00
Andre_601 edfc977730 Update MODRINTH.md 2022-10-05 17:40:01 +02:00
Andre_601 5910c26b4b Add waterfall badge 2022-10-03 23:49:07 +02:00
Andre_601 5b9c506a89 Use jsdelivr url 2022-09-26 00:17:47 +02:00
Andre_601 913ddd7834 Create MODRINTH.md 2022-09-22 13:41:05 +02:00
Andre_601 397c7ea59a Merge pull request #23 from Andre601/feature/readme-new-badges
Prepare readme for new badges
2022-09-21 01:48:30 +02:00
Andre_601 6e7b2541e1 Update README.md 2022-09-21 01:48:22 +02:00
Andre_601 241c15a452 Update README.md 2022-09-20 22:07:40 +02:00
Andre_601 62c5d13aa2 Prepare readme for new badges 2022-09-20 22:05:33 +02:00
Andre601 3a73c5ce6b Fix wrong open-perms for certain OS 2022-09-20 00:41:33 +02:00
Andre_601 bf54e8eb0b Yes I update the README a lot today... 2022-09-18 01:45:46 +02:00
Andre_601 aa2cabedbf Update README.md 2022-09-18 01:09:01 +02:00
Andre_601 66878626e1 Update README.md 2022-09-17 23:35:34 +02:00
Andre_601 f6f3f3d8b6 Update README.md 2022-09-17 23:25:48 +02:00
Andre601 85e41b6c7e Fix wrong formatted messages in /asl reload 2022-09-17 22:51:33 +02:00
Andre_601 a220efc624 Update README.md 2022-09-17 17:58:49 +02:00
Andre_601 ea8871ce8f Update README.md 2022-09-17 17:50:32 +02:00
Andre_601 ea0fdc3798 Update README.md 2022-09-17 17:50:06 +02:00
Andre_601 1188ab0c9c Create config.yml 2022-09-15 01:52:30 +02:00
Andre_601 aaefd4c928 Create feature_request.yml 2022-09-15 01:50:28 +02:00
Andre_601 940bf2fb9d fix typo 2022-09-15 01:20:23 +02:00
Andre_601 0c24bf8d24 Update bug_report.yml 2022-09-15 01:19:36 +02:00
Andre_601 564ce7a820 Create bug_report.yml 2022-09-15 01:18:54 +02:00
Andre_601 bb8cd21b86 Merge pull request #22 from Andre601/dependabot/maven/velocity/org.apache.maven.plugins-maven-shade-plugin-3.4.0
Bump maven-shade-plugin from 3.3.0 to 3.4.0 in /velocity
2022-09-14 18:33:57 +02:00
Andre_601 2d2d8a6175 Merge pull request #20 from Andre601/dependabot/maven/bungeecord/org.apache.maven.plugins-maven-shade-plugin-3.4.0
Bump maven-shade-plugin from 3.3.0 to 3.4.0 in /bungeecord
2022-09-14 18:33:48 +02:00
Andre_601 fcd4bdbe14 Merge pull request #19 from Andre601/dependabot/maven/paper/org.apache.maven.plugins-maven-shade-plugin-3.4.0
Bump maven-shade-plugin from 3.3.0 to 3.4.0 in /paper
2022-09-14 18:33:37 +02:00
Andre_601 3a9b2a7c5a Merge pull request #18 from Andre601/dependabot/maven/core/org.apache.maven.plugins-maven-shade-plugin-3.4.0
Bump maven-shade-plugin from 3.3.0 to 3.4.0 in /core
2022-09-14 18:33:27 +02:00
dependabot[bot] 3b3793ab9a Bump maven-shade-plugin from 3.3.0 to 3.4.0 in /velocity
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.3.0 to 3.4.0.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.3.0...maven-shade-plugin-3.4.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-14 16:33:17 +00:00
Andre_601 d679694d6e Merge pull request #21 from Andre601/dependabot/maven/spigot/org.apache.maven.plugins-maven-shade-plugin-3.4.0
Bump maven-shade-plugin from 3.3.0 to 3.4.0 in /spigot
2022-09-14 18:33:09 +02:00
dependabot[bot] 47405f7492 Bump maven-shade-plugin from 3.3.0 to 3.4.0 in /spigot
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.3.0 to 3.4.0.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.3.0...maven-shade-plugin-3.4.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-14 16:31:57 +00:00
dependabot[bot] af7408dcd5 Bump maven-shade-plugin from 3.3.0 to 3.4.0 in /bungeecord
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.3.0 to 3.4.0.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.3.0...maven-shade-plugin-3.4.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-14 16:31:30 +00:00
dependabot[bot] 17d0930195 Bump maven-shade-plugin from 3.3.0 to 3.4.0 in /paper
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.3.0 to 3.4.0.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.3.0...maven-shade-plugin-3.4.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-14 16:31:28 +00:00
dependabot[bot] 91cd3b6032 Bump maven-shade-plugin from 3.3.0 to 3.4.0 in /core
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.3.0 to 3.4.0.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.3.0...maven-shade-plugin-3.4.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-14 16:31:26 +00:00
Andre601 2d67145ae5 Bump version 2022-09-09 23:33:22 +02:00
Andre601 ba1dd8a70d Fix clearFaviconCache() + expression error 2022-09-09 23:33:00 +02:00
Andre_601 4ea77f2907 Update README.md 2022-09-09 15:11:42 +02:00
Andre_601 7fdc23baa1 Update release.yml 2022-09-09 14:52:13 +02:00
Andre601 d630b472ca Simplify validation check 2022-09-09 01:39:15 +02:00
Andre_601 99509b538b Merge pull request #16 from Andre601/feature/improve-fakeplayers-creation
Improve handling of fake players
2022-09-08 15:27:17 +02:00
Andre601 4539fe4628 Bump version 2022-09-08 15:25:31 +02:00
Andre601 8c81f4f5c8 Rewrite server list profile 2022-09-08 15:19:25 +02:00
Andre601 f34ef3fbe4 Add missing getPlugin() call 2022-09-08 14:16:30 +02:00
Andre601 45f68294c1 Use methods from PluginCore 2022-09-07 21:49:05 +02:00
Andre601 f4056e51ef Rename getPath to getFolderPath 2022-09-07 21:36:35 +02:00
Andre601 7f38c0f224 Update FaviconHandler 2022-09-07 21:34:33 +02:00
Andre601 17a4ba0f8c Improve Fake player creation 2022-09-07 21:33:29 +02:00
Andre_601 5cb1245126 Update README.md 2022-09-05 21:27:42 +02:00
Andre_601 0963d9ffb4 Don't feature a release 2022-09-05 13:50:30 +02:00
Andre_601 6dedd9368f Use release-title 2022-09-05 13:33:55 +02:00
Andre601 f2df3727a7 Add getFaviconHandler to PluginCore
Also, make bStats rely on global config
2022-09-05 13:29:10 +02:00
Andre_601 2b7c24daa4 Create FUNDING.yml 2022-09-04 15:15:54 +02:00
Andre_601 1e042461ac Update README.md 2022-09-04 14:17:42 +02:00
Andre_601 599c2db2bc Re-add modrinth 2022-09-03 16:56:38 +02:00
Andre_601 87505f820c try upload/download artifact 2022-09-03 16:27:44 +02:00
Andre_601 28109c8b5b Add needs to second job 2022-09-03 16:02:45 +02:00
Andre_601 243854592d Merge pull request #15 from Andre601/feature/action-improvements
Make Jar build only happen once
2022-09-03 16:01:05 +02:00
Andre_601 4648bc30e9 test out with github only for now... 2022-09-03 16:00:57 +02:00
Andre_601 f9a96e5525 typo 2022-09-03 00:28:50 +02:00
Andre_601 d158fb852f Update release.yml 2022-09-03 00:14:11 +02:00
Andre_601 77f95900e2 Use cache 2022-09-03 00:10:00 +02:00
Andre_601 912fec1875 Make Jar build only happen once 2022-09-02 23:44:51 +02:00
Andre_601 dba9ca20a4 Merge pull request #14 from Andre601/dependabot/github_actions/Kir-Antipov/mc-publish-3.2
Bump Kir-Antipov/mc-publish from 3.1 to 3.2
2022-09-02 18:35:37 +02:00
dependabot[bot] 7b94ae52db Bump Kir-Antipov/mc-publish from 3.1 to 3.2
Bumps [Kir-Antipov/mc-publish](https://github.com/Kir-Antipov/mc-publish) from 3.1 to 3.2.
- [Release notes](https://github.com/Kir-Antipov/mc-publish/releases)
- [Commits](https://github.com/Kir-Antipov/mc-publish/compare/v3.1...v3.2)

---
updated-dependencies:
- dependency-name: Kir-Antipov/mc-publish
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-02 15:40:17 +00:00
Andre601 6d3bf50278 Actually bump version -_- 2022-09-02 04:27:50 +02:00
Andre_601 8039088fb9 typo -_- 2022-09-02 03:43:39 +02:00
Andre601 ecaaf7bbf3 get rid of abstraction in ExpressionResult + better command output 2022-09-02 03:37:50 +02:00
Andre_601 dd970cfabf Merge pull request #13 from Andre601/feature/auto-publish-action
Create action for auto-publishing to Modrinth
2022-09-02 03:04:06 +02:00
Andre_601 a888481674 Use | definition for loaders. 2022-09-02 02:25:29 +02:00
Andre_601 ee68eafc63 Don't define changelog (Is added automatically) 2022-09-02 02:24:21 +02:00
Andre_601 f22313a5c8 Add proper credits 2022-09-02 02:01:14 +02:00
Andre_601 9603155722 Create action for auto-publishing to Modrinth 2022-09-02 01:57:53 +02:00
Andre_601 df938485b2 It's JDK 16 2022-08-30 21:32:25 +02:00
Andre_601 0ddda8bd78 Update README.md 2022-08-28 16:39:36 +02:00
Andre_601 56a83e88fb Add files via upload 2022-08-28 16:37:54 +02:00
Andre_601 1947f627ef Update README.md 2022-08-28 01:32:38 +02:00
Andre_601 3c344f9a5d Merge pull request #11 from Andre601/feature/improved-favicon-handling
Better Favicon handling
2022-08-27 21:32:01 +02:00
Andre601 8e2004cb81 Move relocations to platform modules 2022-08-27 21:29:57 +02:00
Andre601 004d723c81 Remove minimizeJar to fix ClassNotFoundExceptions 2022-08-27 21:08:24 +02:00
Andre601 90fb6a2654 Add clear favicon function and better favicon handling 2022-08-26 23:33:30 +02:00
Andre_601 0eb5daac81 Update README.md 2022-08-23 00:39:50 +02:00
Andre_601 f23f282ba9 Merge pull request #10 from Andre601/dependabot/maven/core/com.github.ben-manes.caffeine-caffeine-3.1.1
Bump caffeine from 1.3.3 to 3.1.1 in /core
2022-08-22 17:48:41 +02:00
dependabot[bot] df705c0c14 Bump caffeine from 1.3.3 to 3.1.1 in /core
Bumps [caffeine](https://github.com/ben-manes/caffeine) from 1.3.3 to 3.1.1.
- [Release notes](https://github.com/ben-manes/caffeine/releases)
- [Commits](https://github.com/ben-manes/caffeine/compare/v1.3.3...v3.1.1)

---
updated-dependencies:
- dependency-name: com.github.ben-manes.caffeine:caffeine
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-22 15:47:24 +00:00
Andre_601 f27202f2df Update README.md 2022-08-22 00:25:32 +02:00
Andre_601 0d84335db9 Merge pull request #9 from Andre601/feature/favicon-support
Add Favicon support for Server list profiles.
2022-08-21 23:32:46 +02:00
Andre601 50f878e39c Add assets 2022-08-21 23:30:59 +02:00
Andre601 b88ee87c96 Add notes to favicon setting 2022-08-21 23:28:11 +02:00
Andre601 a5f6e689f0 Shade in Caffeine 2022-08-21 01:31:20 +02:00
Andre601 dd0bc8a43a Cache the bytes array 2022-08-20 23:53:32 +02:00
Andre601 563362504f Use some kind of caching... I guess? 2022-08-20 23:43:55 +02:00
Andre601 bd95be080e Add missing profile comments 2022-08-18 16:11:04 +02:00
Andre601 2d02d48d3c Add Favicon support for Server list profiles. 2022-08-18 16:05:00 +02:00
Andre_601 15f7b5def3 Update README.md 2022-08-18 00:02:49 +02:00
Andre_601 f37813d5b7 Merge pull request #8 from Andre601/feature/xMore-option
Add xMore profile option
2022-08-17 21:26:17 +02:00
Andre601 93c2dda56b Add xMore profile option 2022-08-17 21:19:38 +02:00
Andre601 55f5132b37 Add oneMore profile option. 2022-08-17 13:55:25 +02:00
Andre_601 f1e48c2a9c Update README.md 2022-08-14 22:08:18 +02:00
Andre_601 de01db4c12 Update README.md 2022-08-12 00:51:51 +02:00
Andre_601 eefb2b44dd Doesn't work 2022-08-12 00:39:16 +02:00
Andre_601 799d06dee1 Add files via upload 2022-08-12 00:38:19 +02:00
Andre_601 b9338a43e0 Create tmp 2022-08-12 00:38:03 +02:00
Andre601 c89c5f0b49 Make naming bit more consistent 2022-08-04 22:06:39 +02:00
Andre601 1fac910ca1 Keep BufferedWriter at default cache size 2022-08-04 22:04:13 +02:00
Andre601 69061f1a75 Bump version 2022-08-04 22:01:30 +02:00
Andre_601 0d93dcf00d Merge pull request #6 from Andre601/feature/better-placeholder-handling
Better placeholder handling (Hopefully)
2022-08-04 22:00:09 +02:00
Andre601 bb2828e47d Use a BufferedWriter for cache file saving. 2022-08-04 21:58:06 +02:00
Andre601 cdcc872f3a Improve a bit 2022-08-04 20:57:39 +02:00
Andre601 f9192001ba Better placeholder handling (Hopefully) 2022-08-04 18:23:17 +02:00
Andre_601 0bd92862db Update README.md 2022-08-04 14:52:19 +02:00
Andre_601 773fbb55c3 Update README.md 2022-08-04 14:50:59 +02:00
Andre_601 ad4c69a269 Merge pull request #5 from Andre601/dependabot/maven/spigot/me.clip-placeholderapi-2.11.2
Bump placeholderapi from 2.11.1 to 2.11.2 in /spigot
2022-08-04 13:24:42 +02:00
Andre_601 b7a2599178 Merge pull request #4 from Andre601/dependabot/maven/paper/me.clip-placeholderapi-2.11.2
Bump placeholderapi from 2.11.1 to 2.11.2 in /paper
2022-08-04 13:24:21 +02:00
dependabot[bot] bd279d3673 Bump placeholderapi from 2.11.1 to 2.11.2 in /spigot
Bumps placeholderapi from 2.11.1 to 2.11.2.

---
updated-dependencies:
- dependency-name: me.clip:placeholderapi
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-04 11:21:00 +00:00
dependabot[bot] 5882372ce6 Bump placeholderapi from 2.11.1 to 2.11.2 in /paper
Bumps placeholderapi from 2.11.1 to 2.11.2.

---
updated-dependencies:
- dependency-name: me.clip:placeholderapi
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-04 11:20:29 +00:00
Andre_601 55d74f1abe Create dependabot.yml 2022-08-04 13:17:50 +02:00
Andre_601 0056a0e958 Delete dependabot.yml 2022-08-04 13:15:04 +02:00
Andre_601 06396a1622 Create dependabot.yml 2022-08-03 22:57:29 +02:00
Andre601 8d7df60398 Update dependencies 2022-08-03 22:43:36 +02:00
Andre_601 6392929298 Merge pull request #3 from Andre601/feature/further-condition-improvements
Further improvements of condition parsing
2022-08-03 13:37:03 +02:00
Andre601 0f7fb9b180 Better message 2022-08-02 23:00:54 +02:00
Andre601 4b24da8409 Further improve condition handling 2022-08-02 22:59:08 +02:00
Andre_601 afc32da0c3 Update README.md 2022-08-02 02:59:50 +02:00
Andre_601 6aaa169a86 Merge pull request #2 from Andre601/feature/improved-condition-parsing
Better condition parsing
2022-08-02 01:44:00 +02:00
Andre601 091a97076e Fix inverted check and add hidePlayers option 2022-08-02 01:43:37 +02:00
Andre601 d79733cafc Pushing changes recommended by @bergerkiller and @bbayu123 2022-08-02 00:56:54 +02:00
Andre601 99793f5c8a Get rid of ConditionsHolder in favour of Expression 2022-08-01 23:41:07 +02:00
Andre601 8d3cd980cf Move ConditionsHolder and Expression to own folder 2022-08-01 23:25:17 +02:00
Andre601 3e40ad34ab Better condition parsing 2022-08-01 23:18:33 +02:00
Andre601 356bc09c09 Version 1.1.0
- Add ${server host} placeholder to show the pinged server address
- Improved Placeholders class
- Improved StringReplacer to have a static method instead of constantly making new instances.
- Update default.yml with better examples and descriptions.
2022-07-27 16:28:00 +02:00
Andre601 0745a213b5 Version 1.0.3
- Fix HEX color issues on SpigotMC servers
- Fix rare edge case where exception is thrown during config creation
- Add safety check for PaperMC version being used on SpigotMC
2022-07-25 18:28:28 +02:00
Andre_601 6c4a57dca9 plugin, not platform 2022-07-14 22:26:15 +02:00
Andre_601 c10241bf16 Update README.md 2022-07-11 16:00:41 +02:00
Andre_601 33e4a133cd Use darker theme color of modrinth 2022-07-11 11:02:26 +02:00
Andre_601 bc2845b7aa Update README.md 2022-07-11 11:01:17 +02:00
Andre_601 810fcd1fc7 Update README.md 2022-06-30 01:34:19 +02:00
Andre601 34f0b4eef8 Add Hangar (may change in the future) 2022-06-28 02:00:32 +02:00
Andre601 3851fa5f67 Add metrics 2022-06-27 17:47:36 +02:00
104 changed files with 5881 additions and 948 deletions

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
patreon: andre_601
ko_fi: andre_601
+41
View File
@@ -0,0 +1,41 @@
name: API support
description: Ask for help with the API.
body:
- type: markdown
attributes:
value: |-
## Before opening
Please make sure you've checked the following things first:
- You're using the latest release of AdvancedServerList [[Download from Modrinth](https://modrinth.com/plugin/advancedserverlist)] and the right API version.
- You're using a supported Proxy/Server version. As of right now is only 1.19.x supported.
- The Feature you suggest hasn't been suggested before.
- type: dropdown
attributes:
label: Platforms
description: On what platform(s) are you using the API?
multiple: true
options:
- Proxy (BungeeCord, Waterfall, ...)
- Proxy (Velocity)
- Server (Spigot, Paper, ...)
validations:
required: true
- type: textarea
attributes:
label: Description
description: |-
Give a detailed description of what exactly you try to do, what steps you did and what you need help with.
placeholder: I tried to...
validations:
required: true
- type: textarea
attributes:
label: Code
description: |-
Provide code-snippets to help us better understand what you tried.
The code is automatically parsed as Java Code.
render: java
validations:
required: true
+62
View File
@@ -0,0 +1,62 @@
name: Bug Report
description: Report an issue with the plugin
body:
- type: markdown
attributes:
value: |-
## Before opening
Please make sure you've checked the following things first:
- You're using the latest release of AdvancedServerList [[Download from Modrinth](https://modrinth.com/plugin/advancedserverlist)]
- You're using a supported Proxy/Server version. As of right now is only 1.19.x supported.
- The issue happens with just AdvancedServerList installed.
- type: dropdown
attributes:
label: Platform
description: On what platform do you use the plugin?
options:
- BungeeCord
- Other (Please specify)
- Paper
- Purpur
- Spigot
- Velocity
- Waterfall
validations:
required: true
- type: input
attributes:
label: Other platform
description: |-
Please provide the name of the platform (Proxy/Server) you use, if you selected `Other (Please specify)` in the above section.
Leave empty otherwise.
placeholder: SomeProxy
- type: textarea
attributes:
label: What happened?
description: |-
Give a detailed description of what exactly happened.
You can provide screenshots to further explain the issues.
When sharing errors, make sure to use a site such as https://paste.helpch.at to do so.
placeholder: When I do ... does ... happen.
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce
description: Provide a detailed step-by-step explanation on how to reproduce this issue.
placeholder: |-
1. Do this
2. ...
3. Profit!
validations:
required: true
- type: textarea
attributes:
label: Server List Profiles
description: |-
**Optional**
Provide any Server List Profiles you use, if you believe those to be the cause of the issue.
This text field will automatically render the text as YAML code.
render: yaml
+8
View File
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Discord Server
about: Ask questions about the plugin in the `#advancedserverlist` channel.
url: https://discord.gg/6dazXp6
- name: Wiki
about: Check the Wiki for explanations about the features and examples.
url: https://github.com/Andre601/AdvancedServerList/wiki
@@ -0,0 +1,34 @@
name: Feature Request
description: Suggest a new feature for the plugin
body:
- type: markdown
attributes:
value: |-
## Before opening
Please make sure you've checked the following things first:
- You're using the latest release of AdvancedServerList [[Download from Modrinth](https://modrinth.com/plugin/advancedserverlist)]
- You're using a supported Proxy/Server version. As of right now is only 1.19.x supported.
- The Feature you suggest hasn't been suggested before.
- type: dropdown
attributes:
label: Platforms
description: What platforms should this feature be usable on
multiple: true
options:
- Proxy (BungeeCord, Waterfall, ...)
- Proxy (Velocity)
- Server (Spigot, Paper, ...)
validations:
required: true
- type: textarea
attributes:
label: Feature
description: |-
Give a detailed description of what exactly should be added and/or changed in AdvancedServerList.
You can provide screenshots to further explain the idea.
When sharing code snippets, make sure to use a site such as https://paste.helpch.at to do so.
placeholder: AdvancedServerList should get ... because ...
validations:
required: true
+96
View File
@@ -0,0 +1,96 @@
# AdvancedServerList
AdvancedServerList is a plugin that allowys you to customize your server's MOTD, favicon, Player count text and player count hover displayed in a player's multiplayer server list based on conditions and priorities.
## Supported Platforms
<a href="https://www.spigotmc.org" target="_blank">
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@2/assets/minimal/supported/spigot_vector.svg" width="64" alt="platform-spigot" title="Tested on Spigot">
</a>
<a href="https://papermc.io" target="_blank">
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@2/assets/minimal/supported/paper_vector.svg" width="64" alt="platform-paper" title="Tested on Paper">
</a>
<a href="https://www.spigotmc.org" target="_blank">
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@2/assets/minimal/supported/bungeecord_vector.svg" width="64" alt="platform-bungeecord" title="Tested on BungeeCord">
</a>
<a href="https://www.papermc.io" target="_blank">
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@2/assets/minimal/supported/waterfall_vector.svg" width="64" alt="platform-waterfall" title="Tested on Waterfall">
</a>
<a href="https://velocitypowered.com" target="_blank">
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@2/assets/minimal/supported/velocity_vector.svg" width="64" alt="platform-velocity" title="Tested on Velocity">
</a>
## What is a Server List Profile?
A Server List Profile refers to a YAML file located in the plugin's `profiles` directory.
It allows you to configure specific aspects of the server display in the MC-Client's multiplayer server list, such as the MOTD, player count text, player count hover or Favicon.
> [*Read More*][profiles]
### Conditions
Thanks to conditions can you determine when a profile should be displayed in the player’s server list, to allow displaying specific text (i.e. a warning when they are using an outdated version).
When multiple profiles exist that have valid conditions will the one with the highest priority (Higher number = higher priority) be selected. Having no conditions makes the profile automatically return true.
> [*Read More*][conditions]
### Placeholders
Placeholders can be used within conditions to further customize them. They can also be used in any other text option (motd, playerCount and players) to display their respective values.
The format is `${<target> <identifier>}` which was adopted from BungeeTabListPlus. Depending on what platform you use the plugin on are only specific placeholders available.
When using the SpigotMC/PaperMC version can you also use placeholders from [PlaceholderAPI][placeholderapi] using its `%<identifier>_<values>%` placeholder format. PlaceholderAPI needs to be installed for that.
> [*Read More*][placeholders]
### Formatting
The plugin uses the MiniMessage format for a consistent, easy way of formatting your text.
Only certain options are usable for each option. As an example does motd support HEX colours and gradients, while the other options only support default colour codes.
> [*Read More*][minimessage]
## Dependencies
| Dependency | Platforms | Required? |
| ---------------- | ------------- | --------- |
| [ProtocolLib] | Spigot | Yes |
| [PlaceholderAPI] | Spigot, Paper | No |
## Statistics
This plugin sends statistics to [bStats] to display.
You can disable this in the global bStats config file located in `/plugins/bstats/`
- [BungeeCord][bstats-bungee]
- [Paper/Spigot][bstats-spigot]
- [Velocity][bstats-velocity]
## Screenshots
<a href="https://modrinth.com/plugin/advancedserverlist/gallery" target="_blank">
<img src="https://raw.githubusercontent.com/intergrav/devins-badges/v2/assets/cozy/documentation/modrinth-gallery_vector.svg" width="208" alt="gallery" title="Check out the Gallery">
</a>
## Support
<a href="https://discord.gg/6dazXp6" target="_blank">
<img src="https://raw.githubusercontent.com/intergrav/devins-badges/v2/assets/minimal/social/discord-singular_vector.svg" width="64" alt="discord" title="Join my Discord Server">
</a>
<a href="https://app.revolt.chat/invite/74TpERXA" target="_blank">
<img src="https://raw.githubusercontent.com/intergrav/devins-badges/v2/assets/minimal/social/revolt-singular_vector.svg" width="64" alt="revolt" title="Join my Revolt Server">
</a>
<a href="https://blobfox.coffee/@andre_601" target="_blank">
<img src="https://raw.githubusercontent.com/intergrav/devins-badges/v2/assets/minimal/social/mastodon-singular_vector.svg" width="64" alt="revolt" title="Chat with me on Mastodon">
</a>
<!-- Links -->
[profiles]: https://github.com/Andre601/AdvancedServerList/wiki/Profiles
[conditions]: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#conditions
[placeholders]: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#placeholders
[minimessage]: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#minimessage
[placeholderapi]: https://www.spigotmc.org/resources/6245/
[modrinth]: https://modrinth.com/plugin/advancedserverlist
[protocollib]: https://www.spigotmc.org/resources/1997/
[placeholderapi]: https://www.spigotmc.org/resources/6245/
[bstats]: https://bstats.org
[bstats-bungee]: https://bstats.org/plugin/bungeecord/AdvancedServerList/15585
[bstats-spigot]: https://bstats.org/plugin/bukkit/AdvancedServerList/15584
[bstats-velocity]: https://bstats.org/plugin/velocity/AdvancedServerList/15587
[gallery]: https://modrinth.com/mod/advancedserverlist/gallery
Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

+50
View File
@@ -0,0 +1,50 @@
version: 2
updates:
- package-ecosystem: maven
directory: "/bungeecord"
schedule:
interval: daily
open-pull-requests-limit: 10
labels:
- 'Type: Update Dependency'
- 'Target: BungeeCord'
- package-ecosystem: maven
directory: "/core"
schedule:
interval: daily
open-pull-requests-limit: 10
labels:
- 'Type: Update Dependency'
- 'Target: Core'
- package-ecosystem: maven
directory: "/paper"
schedule:
interval: daily
open-pull-requests-limit: 10
labels:
- 'Type: Update Dependency'
- 'Target: PaperMC'
- package-ecosystem: maven
directory: "/spigot"
schedule:
interval: daily
open-pull-requests-limit: 10
labels:
- 'Type: Update Dependency'
- 'Target: Spigot'
- package-ecosystem: maven
directory: "/velocity"
schedule:
interval: daily
open-pull-requests-limit: 10
labels:
- 'Type: Update Dependency'
- 'Target: Velocity'
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10
labels:
- 'Type: Update Dependency'
- 'Target: GitHub Action'
+30
View File
@@ -0,0 +1,30 @@
name: Deploy Site
on:
push:
paths:
- 'docs/**'
- 'mkdocs.yml'
branches:
- master
tags-ignore:
- '**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.7
uses: actions/setup-python@v4
with:
python-version: 3.7
- name: Install dependencies
run: |
python -m pip install --upgrade pip setuptools
python -m pip install -r requirements.txt
- name: Deploy Files
run: |
git config user.name "github-actions[bot]"
git config user.email "actions@github.com"
mkdocs gh-deploy --force
+91 -9
View File
@@ -1,23 +1,105 @@
name: Build Jars
#
# Big thank you to @Kir-Antipov for providing this workflow file.
# Check out his mc-publish action here: https://github.com/Kir-Antipov/mc-publish
#
name: Publish Assets
on:
release:
types: [published]
types:
- published
jobs:
build:
buildJars:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 1.11
- name: Set up JDK 1.16
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '16'
- name: Build jar
cache: 'maven'
- name: Build Jar files
run: mvn clean install
- name: Upload jar
uses: AButler/upload-release-assets@v2.0
- name: Save BungeeCord Jar file
uses: actions/upload-artifact@v3
with:
files: 'bungeecord/target/AdvancedServerList-*.jar;paper/target/AdvancedServerList-*.jar;spigot/target/AdvancedServerList-*.jar;velocity/target/AdvancedServerList-*.jar'
repo-token: ${{ secrets.GITHUB_TOKEN }}
name: asl-bungeecord
path: bungeecord/target/AdvancedServerList-*.jar
if-no-files-found: error
retention-days: 1
- name: Save Paper Jar file
uses: actions/upload-artifact@v3
with:
name: asl-paper
path: paper/target/AdvancedServerList-*.jar
if-no-files-found: error
retention-days: 1
- name: Save Spigot Jar file
uses: actions/upload-artifact@v3
with:
name: asl-spigot
path: spigot/target/AdvancedServerList-*.jar
if-no-files-found: error
retention-days: 1
- name: Save Velocity Jar file
uses: actions/upload-artifact@v3
with:
name: asl-velocity
path: velocity/target/AdvancedServerList-*.jar
if-no-files-found: error
retention-days: 1
publish:
needs: [buildJars]
strategy:
matrix:
include:
- platforms: BungeeCord, Waterfall
directory: bungeecord
loaders: |
bungeecord
waterfall
- platforms: Paper
directory: paper
loaders: paper
- platforms: Spigot
directory: spigot
loaders: spigot
- platforms: Velocity
directory: velocity
loaders: velocity
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Get Jar files
uses: actions/download-artifact@v3
with:
name: asl-${{ matrix.directory }}
path: ${{ matrix.directory }}/target
- name: Get Version
id: github-release
run: |
version=$(echo ${{ github.event.release.tag_name }} | cut -d'v' -f2)
echo "PLUGIN_RELEASE=$version" >> $GITHUB_OUTPUT
- name: Upload AdvancedServerList (${{ matrix.loaders }}) to GitHub and Modrinth
uses: Kir-Antipov/mc-publish@v3.2
with:
name: ${{ github.event.release.tag_name }} (${{ matrix.platforms }})
version: ${{ steps.github-release.outputs.PLUGIN_RELEASE }}
version-type: release
files: ${{ matrix.directory}}/target/AdvancedServerList-*.jar
files-secondary: ""
loaders: ${{ matrix.loaders }}
version-resolver: releases
game-versions: |
1.19
1.19.1
1.19.2
1.19.3
java: 16
modrinth-id: xss83sOY
modrinth-featured: false
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
github-token: ${{ secrets.GITHUB_TOKEN }}
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2022 Andre_601
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
+94 -35
View File
@@ -1,45 +1,104 @@
[placeholders]: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#placeholders
[wiki]: https://github.com/Andre601/AdvancedServerList/wiki
[spigot]: https://www.spigotmc.org/resources/102910/
[stats-spigot]: https://bstats.org/plugin/bukkit/AdvancedServerList/15584
[stats-bungeecord]: https://bstats.org/plugin/bungeecord/AdvancedServerList/15585
[stats-velocity]: https://bstats.org/plugin/velocity/AdvancedServerList/15587
[license]: https://github.com/Andre601/AdvancedServerList/blob/master/LICENSE
# AdvancedServerList
<a href="https://modrinth.com/plugin/advancedserverlist" target="_blank">
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@2/assets/cozy/available/modrinth_vector.svg" height="64" align="right" alt="modrinth" title="Available on Modrinth">
</a>
AdvancedServerList is a plugin available for Spigot, PaperMC, BungeeCord and Velocity with the goal of allowing the creation of multiple "profiles" to display a custom MOTD, Player count and Hover in the Multiplayer Server list.
AdvancedServerList is a plugin that allowys you to customize your server's MOTD, favicon, Player count text and player count hover displayed in a player's multiplayer server list based on conditions and priorities.
## Features
## Supported Platforms
- Create multiple profiles with
- different priority
- different conditions
- custom MOTD
- custom Player count text
- custom hover for the player count text
- Placeholder support [[Read More][placeholders]]
<a href="https://www.spigotmc.org" target="_blank">
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@2/assets/minimal/supported/spigot_vector.svg" height="64" alt="platform-spigot" title="Tested on Spigot">
</a>
<a href="https://papermc.io" target="_blank">
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@2/assets/minimal/supported/paper_vector.svg" height="64" alt="platform-paper" title="Tested on Paper">
</a>
<a href="https://www.spigotmc.org" target="_blank">
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@2/assets/minimal/supported/bungeecord_vector.svg" height="64" alt="platform-bungeecord" title="Tested on BungeeCord">
</a>
<a href="https://www.papermc.io" target="_blank">
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@2/assets/minimal/supported/waterfall_vector.svg" height="64" alt="platform-waterfall" title="Tested on Waterfall">
</a>
<a href="https://velocitypowered.com" target="_blank">
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@2/assets/minimal/supported/velocity_vector.svg" height="64" alt="platform-velocity" title="Tested on Velocity">
</a>
## Platforms
## What is a Server List Profile?
A Server List Profile refers to a YAML file located in the plugin's `profiles` directory.
It allows you to configure specific aspects of the server display in the MC-Client's multiplayer server list, such as the MOTD, player count text, player count hover or Favicon.
AdvancedServerList currently provides dedicated Jar files for the following platforms:
> [*Read More*][profiles]
- Spigot
- PaperMC
- BungeeCord
- Velocity
### Conditions
Thanks to conditions can you determine when a profile should be displayed in the player’s server list, to allow displaying specific text (i.e. a warning when they are using an outdated version).
Other platform may be supported, but compatability has not been tested with any other platform/fork not mentioned above.
When multiple profiles exist that have valid conditions will the one with the highest priority (Higher number = higher priority) be selected. Having no conditions makes the profile automatically return true.
## Links
> [*Read More*][conditions]
- [SpigotMC][spigot]
- [Wiki][wiki]
- Statistics
- [Spigot/PaperMC][stats-spigot]
- [BungeeCord][stats-bungeecord]
- [Velocity][stats-velocity]
- [License][license]
### Placeholders
Placeholders can be used within conditions to further customize them. They can also be used in any other text option (motd, playerCount and players) to display their respective values.
The format is `${<target> <identifier>}` which was adopted from BungeeTabListPlus. Depending on what platform you use the plugin on are only specific placeholders available.
When using the SpigotMC/PaperMC version can you also use placeholders from [PlaceholderAPI][placeholderapi] using its `%<identifier>_<values>%` placeholder format. PlaceholderAPI needs to be installed for that.
If you're a plugin dev can you even add your own custom placeholders through AdvancedServerList's own API.
> [*Read More*][placeholders]
### Formatting
The plugin uses the MiniMessage format for a consistent, easy way of formatting your text.
Only certain options are usable for each option. As an example does motd support HEX colours and gradients, while the other options only support default colour codes.
> [*Read More*][minimessage]
## Dependencies
| Dependency | Platforms | Required? |
| ---------------- | ------------- | --------- |
| [ProtocolLib] | Spigot | Yes |
| [PlaceholderAPI] | Spigot, Paper | No |
## Statistics
This plugin sends statistics to [bStats] to display.
You can disable this in the global bStats config file located in `/plugins/bstats/`
- [BungeeCord][bstats-bungee]
- [Paper/Spigot][bstats-spigot]
- [Velocity][bstats-velocity]
## Screenshots
<a href="https://modrinth.com/plugin/advancedserverlist/gallery" target="_blank">
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@2/assets/cozy/documentation/modrinth-gallery_vector.svg" height="64" alt="gallery" title="Check out the Gallery">
</a>
## Support
<a href="https://discord.gg/6dazXp6" target="_blank">
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@2/assets/minimal/social/discord-singular_vector.svg" height="64" alt="discord" title="Join my Discord Server">
</a>
<a href="https://app.revolt.chat/invite/74TpERXA" target="_blank">
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@2/assets/minimal/social/revolt-singular_vector.svg" height="64" alt="revolt" title="Join my Revolt Server">
</a>
<a href="https://blobfox.coffee/@andre_601" target="_blank">
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@2/assets/minimal/social/mastodon-singular_vector.svg" height="64" alt="mastodon" title="Chat with me on Mastodon">
</a>
<!-- Links -->
[profiles]: https://www.andre601.ch/AdvancedServerList/profiles/
[conditions]: https://www.andre601.ch/AdvancedServerList/profiles/#conditions
[placeholders]: https://www.andre601.ch/AdvancedServerList/profiles/placeholders/
[minimessage]: https://www.andre601.ch/AdvancedServerList/profiles/formatting/
[modrinth]: https://modrinth.com/plugin/advancedserverlist
[protocollib]: https://www.spigotmc.org/resources/1997/
[placeholderapi]: https://www.spigotmc.org/resources/6245/
[bstats]: https://bstats.org
[bstats-bungee]: https://bstats.org/plugin/bungeecord/AdvancedServerList/15585
[bstats-spigot]: https://bstats.org/plugin/bukkit/AdvancedServerList/15584
[bstats-velocity]: https://bstats.org/plugin/velocity/AdvancedServerList/15587
[gallery]: https://modrinth.com/mod/advancedserverlist/gallery
+59
View File
@@ -0,0 +1,59 @@
<?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>api</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>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<finalName>AdvancedServerList-API-${plugin.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,98 @@
/*
* 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.api;
import ch.andre601.advancedserverlist.api.internal.CheckUtil;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
* Core class of the API for AdvancedServerList.
* <br>Use {@link #get() get()} to retrieve the instance currently used.
*/
public class AdvancedServerListAPI{
private static AdvancedServerListAPI instance;
private final Map<String, PlaceholderProvider> placeholderProviders = new HashMap<>();
private AdvancedServerListAPI(){}
/**
* Retrieves the instance used of this API.
* <br>If no instance has been made so far will a new one be created.
*
* @return Instance of this API.
*/
public static AdvancedServerListAPI get(){
if(instance != null)
return instance;
return (instance = new AdvancedServerListAPI());
}
/**
* Adds the provided {@link PlaceholderProvider PlaceholderProvider} to the list, if it passes the following checks:
* <ul>
* <li>The identifier is not null or 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
* {@link ch.andre601.advancedserverlist.api.exceptions.InvalidPlaceholderProviderException InvalidPlaceholderProviderException}
* being thrown.
*
* @param placeholderProvider
* The {@link PlaceholderProvider PlaceholderProvider} to add.
*
* @throws ch.andre601.advancedserverlist.api.exceptions.InvalidPlaceholderProviderException
* When the provided {@link PlaceholderProvider PlaceholderProvider instance} has a null or empty identifier,
* the identifier contains spaces or another provider with the same identifier is already loaded.
*/
public void addPlaceholderProvider(PlaceholderProvider placeholderProvider){
CheckUtil.notEmpty(placeholderProvider.getIdentifier(), "Identifier");
String identifier = placeholderProvider.getIdentifier().toLowerCase(Locale.ROOT);
CheckUtil.check(identifier.contains(" "), "Identifier may not contain spaces.");
CheckUtil.check(placeholderProviders.containsKey(identifier), "A PlaceholderProvider with name " + identifier + " already exists.");
placeholderProviders.put(identifier, placeholderProvider);
}
/**
* Retrieves the {@link PlaceholderProvider PlaceholderProvider} associated with the given identifier, or {@code null}
* should no such entry exist.
*
* @param identifier
* The identifier to find a matching {@link PlaceholderProvider PlaceholderProvider} for.
*
* @return Possibly-null {@link PlaceholderProvider PlaceholderProvider instance}.
*/
public PlaceholderProvider retrievePlaceholderProvider(String identifier){
return placeholderProviders.get(identifier.toLowerCase(Locale.ROOT));
}
}
@@ -0,0 +1,97 @@
/*
* 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.api;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
/**
* Abstract class that is used to provide your own Placeholder patterns for AdvancedServerList to parse.
*
* <p>In order for your class to be considered a valid PlaceholderProvider will you need to set the
* {@link #identifier identifier} to a non-null, non-empty value without having any spaces in it.
* <br>Once set, use {@link ch.andre601.advancedserverlist.api.AdvancedServerListAPI#addPlaceholderProvider(PlaceholderProvider) AdvancedServerListAPI#addPlaceholderProvider(PlaceholderProvider)}
* to register your class for AdvancedServerList to use.
*/
public abstract class PlaceholderProvider{
private final String identifier;
/**
* Constructor used to set the identifier for the class extending the PlaceholderProvider class.
*
* <h2>Example</h2>
* <pre>{@code
* public class MyPlaceholders extends PlaceholderProvider {
*
* public MyPlaceholders() {
* super("myplaceholders");
* }
*
* @Override
* public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server) {
* if(placeholder.equals("hello")
* return "Hi!";
*
* return null;
* }
* }
* }</pre>
*
* @param identifier
* The identifier to use for the placeholder. Shouldn't be null nor empty.
*/
public PlaceholderProvider(String identifier){
this.identifier = identifier;
}
/**
* Method called by AdvancedServerList's StringReplacer class to replace any appearances of
* {@code ${<identifier> <placeholder>}} with whatever value a PlaceholderProvider may return.
*
* <p>Returning {@code null} will be treated as an invalid placeholder, returning the full placeholder as-is without
* any changes made.
*
* @param placeholder
* The part of the placeholder after the identifier ({@code ${<identifier> <placeholder>}}
* @param player
* The {@link GenericPlayer GenericPlayer instance} used.
* @param server
* The {@link GenericServer GenericServer instance} used.
*
* @return Parsed String based on the PlaceholderProvider or {@code null} for invalid placeholders.
*/
public abstract String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server);
/**
* Returns the identifier used by this instance.
*
* @return String containing the identifier used by this instance.
*/
public String getIdentifier(){
return identifier;
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -23,14 +23,15 @@
*
*/
package ch.andre601.advancedserverlist.core.profiles.replacer;
package ch.andre601.advancedserverlist.api.exceptions;
public class Placeholders{
/**
* RuntimeException thrown whenever an invalid {@link ch.andre601.advancedserverlist.api.PlaceholderProvider PlaceholderProvider}
* has been given.
*/
public class InvalidPlaceholderProviderException extends RuntimeException{
public static final String PLAYERS_ONLINE = "${players online}";
public static final String PLAYERS_MAX = "${players max}";
public static final String PLAYER_NAME = "${player name}";
public static final String PLAYER_PROTOCOL = "${player protocol}";
public static final String PLAYER_VERSION = "${player version}";
public InvalidPlaceholderProviderException(String msg){
super(msg);
}
}
@@ -0,0 +1,40 @@
/*
* 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.api.internal;
import ch.andre601.advancedserverlist.api.exceptions.InvalidPlaceholderProviderException;
public class CheckUtil{
public static void notEmpty(String text, String name){
check(text == null || text.isEmpty(), name + " may not be null or empty.");
}
public static void check(boolean condition, String msg){
if(condition)
throw new InvalidPlaceholderProviderException(msg);
}
}
@@ -0,0 +1,47 @@
/*
* 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.api.internal;
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
public class ServerPlaceholders extends PlaceholderProvider{
public ServerPlaceholders(){
super("server");
}
@Override
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
return switch(placeholder){
case "playersOnline" -> String.valueOf(server.getPlayersOnline());
case "playersMax" -> String.valueOf(server.getPlayersMax());
case "host" -> server.getHost();
default -> null;
};
}
}
@@ -0,0 +1,136 @@
/*
* 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.api.objects;
import java.util.UUID;
/**
* A basic abstract class used to store generic player data with to use in placeholder resolving.
* <br>The different variants of AdvancedServerList extend their own class with this one and may or may not use all values
* available here.
*
* <p>The following values will always be present, no matter the platform this class is used on:
* <ul>
* <li>{@link #getName() Name of Player}</li>
* <li>{@link #getProtocol() Protocol version of Player}</li>
* <li>{@link #getUUID() UUID of Player}</li>
* </ul>
* Any other value is not guaranteed to be accurate or even be set.
*/
public abstract class GenericPlayer{
protected String name = null;
protected int protocol = -1;
protected UUID uuid = null;
protected String version = null;
protected boolean playedBefore = false;
protected boolean banned = false;
protected boolean whitelisted = false;
/**
* Returns the name of the player.
* <br>Note that on Spigot, Paper and other forks can the name differ from the one cached by AdvancedServerList,
* if the plugin was able to retrieve an OfflinePlayer instance from the server. On BungeeCord and Velocity will
* the returned String always be the name from the cache.
*
* <p>Name may be whatever has been defined in AdvancedServerList's config.yml, should the player not be cached yet
* by AdvancedServerList.
*
* @return String representing the player's name.
*/
public String getName(){
return name;
}
/**
* Returns the protocol ID the player is using. The protocol ID is an integer used by Minecraft to determine what
* version a server or client is running.
*
* @return Integer representing the protocol version of this player.
*/
public int getProtocol(){
return protocol;
}
/**
* Returns the Unique ID associated with this player.
*
* <p>UUID may be whatever has been defined in AdvancedServerList's config.yml, should the player not be cached yet
* by AdvancedServerList.
*
* @return UUID of the player.
*/
public UUID getUUID(){
return uuid;
}
/**
* Returns the {@link #getProtocol() protocol version} in a readable MC version format (i.e. 1.19.3).
*
* <p>This only works on Velocity and will return {@code null} for any other platform.
*
* @return The readable MC version the player uses.
*/
public String getVersion(){
return version;
}
/**
* Returns whether this player has played on the Server before.
*
* <p>The returned boolean is <b>always</b> false on BungeeCord and Velocity due to a lack of any offline player data
* storage.
*
* @return Boolean indicating whether this player has played on this Server before.
*/
public boolean hasPlayedBefore() {
return playedBefore;
}
/**
* Returns whether this player is banned on the server.
*
* <p>The returned boolean is <b>always</b> false on BungeeCord and Velocity due to a lack of any offline player data
* storage.
*
* @return Boolean indicating whether this player was banned from the server.
*/
public boolean isBanned(){
return banned;
}
/**
* Returns whether this player is whitelisted on the server.
*
* <p>The returned boolean is <b>always</b> false on BungeeCord and Velocity due to a lack of any offline player data
* storage.
*
* @return Boolean indicating whether this player was banned from the server.
*/
public boolean isWhitelisted(){
return whitelisted;
}
}
@@ -0,0 +1,70 @@
/*
* 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.api.objects;
/**
* Simple class used to wrap around some generic server data such as online player count, amount of total players that
* can join and the host (IP/Domain) that got pinged by the player.
*/
public class GenericServer{
private final int playersOnline;
private final int playersMax;
private final String host;
public GenericServer(int playersOnline, int playersMax, String host){
this.playersOnline = playersOnline;
this.playersMax = playersMax;
this.host = host;
}
/**
* Returns the number of players currently online on the server.
*
* @return Number of players online on the server.
*/
public int getPlayersOnline(){
return playersOnline;
}
/**
* Returns the number of total players that can join the server.
*
* @return Number of total players that can join the server.
*/
public int getPlayersMax(){
return playersMax;
}
/**
* Returns the IP/Domain that got pinged by the player.
*
* @return Possibly-null String containing the IP/Domain that got pinged by the player.
*/
public String getHost(){
return host;
}
}
+15 -7
View File
@@ -2,7 +2,7 @@
<!--
~ MIT License
~
~ Copyright (c) 2022 Andre_601
~ 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
@@ -75,7 +75,7 @@
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-platform-bungeecord</artifactId>
<version>4.1.0</version>
<version>4.1.2</version>
<scope>compile</scope>
</dependency>
<dependency>
@@ -103,22 +103,30 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<version>3.4.1</version>
<configuration>
<minimizeJar>true</minimizeJar>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>org.spongepowered.configurate</pattern>
<shadedPattern>ch.andre601.advancedserverlist.velocity.depends.configurate</shadedPattern>
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.configurate</shadedPattern>
</relocation>
<relocation>
<pattern>net.kyori.adventure</pattern>
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.adventure</shadedPattern>
<pattern>net.kyori</pattern>
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.kyori</shadedPattern>
</relocation>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.bstats</shadedPattern>
</relocation>
<relocation>
<pattern>com.github.benmanes.caffeine</pattern>
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.caffeine</shadedPattern>
</relocation>
<relocation>
<pattern>okhttp3</pattern>
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.okhttp3</shadedPattern>
</relocation>
</relocations>
<filters>
<filter>
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -25,26 +25,38 @@
package ch.andre601.advancedserverlist.bungeecord;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.bungeecord.commands.CmdAdvancedServerList;
import ch.andre601.advancedserverlist.bungeecord.events.JoinEvent;
import ch.andre601.advancedserverlist.bungeecord.events.PingEvent;
import ch.andre601.advancedserverlist.bungeecord.logging.BungeeLogger;
import ch.andre601.advancedserverlist.bungeecord.objects.BungeePlayer;
import ch.andre601.advancedserverlist.bungeecord.objects.PlayerPlaceholders;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.interfaces.PluginCore;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler;
import net.md_5.bungee.api.Favicon;
import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.plugin.Plugin;
import org.bstats.bungeecord.Metrics;
import org.bstats.charts.SimplePie;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class BungeeCordCore extends Plugin implements PluginCore{
public class BungeeCordCore extends Plugin implements PluginCore<Favicon, ServerPing.PlayerInfo, BungeePlayer>{
private AdvancedServerList core;
private FaviconHandler<Favicon> faviconHandler = null;
private final PluginLogger logger = new BungeeLogger(getLogger());
@Override
public void onEnable(){
this.core = new AdvancedServerList(this);
this.core = new AdvancedServerList(this, new PlayerPlaceholders());
}
@Override
@@ -65,7 +77,18 @@ public class BungeeCordCore extends Plugin implements PluginCore{
@Override
public void loadMetrics(){
new Metrics(this, 15585);
new Metrics(this, 15585).addCustomChart(new SimplePie("profiles",
() -> String.valueOf(core.getFileHandler().getProfiles().size())
));
}
@Override
public void clearFaviconCache(){
if(faviconHandler == null)
return;
faviconHandler.clearCache();
}
@Override
@@ -74,7 +97,7 @@ public class BungeeCordCore extends Plugin implements PluginCore{
}
@Override
public Path getPath(){
public Path getFolderPath(){
return getDataFolder().toPath();
}
@@ -83,6 +106,14 @@ public class BungeeCordCore extends Plugin implements PluginCore{
return logger;
}
@Override
public FaviconHandler<Favicon> getFaviconHandler(){
if(faviconHandler == null)
faviconHandler = new FaviconHandler<>(core);
return faviconHandler;
}
@Override
public String getPlatformName(){
return getProxy().getName();
@@ -92,4 +123,24 @@ public class BungeeCordCore extends Plugin implements PluginCore{
public String getPlatformVersion(){
return getProxy().getVersion();
}
@Override
public String getLoader(){
return "bungeecord";
}
@Override
public List<ServerPing.PlayerInfo> createPlayers(List<String> lines, BungeePlayer player, GenericServer server){
List<ServerPing.PlayerInfo> players = new ArrayList<>(lines.size());
for(String line : lines){
String parsed = ComponentParser.text(line)
.applyReplacements(player, server)
.toString();
players.add(new ServerPing.PlayerInfo(parsed, UUID.randomUUID()));
}
return players;
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -0,0 +1,153 @@
/*
* 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.events;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
import ch.andre601.advancedserverlist.bungeecord.objects.BungeePlayer;
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
import ch.andre601.advancedserverlist.core.interfaces.events.GenericEventWrapper;
import ch.andre601.advancedserverlist.core.objects.CachedPlayer;
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.event.ProxyPingEvent;
import java.awt.image.BufferedImage;
import java.net.InetSocketAddress;
import java.util.List;
public class BungeeEventWrapper implements GenericEventWrapper<Favicon, ServerPing.PlayerInfo, BungeePlayer>{
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 void setMaxPlayers(int maxPlayers){
ping.getPlayers().setMax(maxPlayers);
}
@Override
public void setMotd(Component component){
ping.setDescriptionComponent(new TextComponent(BungeeComponentSerializer.get().serialize(component)));
}
@Override
public void hidePlayers(){
ping.setPlayers(null);
}
@Override
public void setPlayerCount(String name){
protocol.setName(name);
protocol.setProtocol(-1);
}
@Override
public void setPlayers(List<String> players, BungeePlayer player, GenericServer server){
ServerPing.PlayerInfo[] playerInfos = plugin.createPlayers(players, player, server).toArray(new ServerPing.PlayerInfo[0]);
if(playerInfos.length > 0)
ping.getPlayers().setSample(playerInfos);
}
@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 int getProtocolVersion(){
return this.protocol.getProtocol();
}
@Override
public int getOnlinePlayers(){
return ping.getPlayers().getOnline();
}
@Override
public int getMaxPlayers(){
return ping.getPlayers().getMax();
}
@Override
public String getPlayerIP(){
return ((InetSocketAddress)event.getConnection().getSocketAddress()).getHostString();
}
@Override
public String parsePAPIPlaceholders(String text, BungeePlayer player){
return text;
}
@Override
public String getVirtualHost(){
return this.resolveHost(event.getConnection().getVirtualHost());
}
@Override
public PluginCore<Favicon, ServerPing.PlayerInfo, BungeePlayer> getPlugin(){
return plugin;
}
@Override
public BungeePlayer createPlayer(CachedPlayer player, int protocol){
return new BungeePlayer(player, protocol);
}
@Override
public Favicon createFavicon(BufferedImage image) throws IllegalArgumentException{
return Favicon.create(image);
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -26,6 +26,7 @@
package ch.andre601.advancedserverlist.bungeecord.events;
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
@@ -44,6 +45,8 @@ public class JoinEvent implements Listener{
@EventHandler
public void onJoin(PostLoginEvent event){
InetSocketAddress address = (InetSocketAddress)event.getPlayer().getPendingConnection().getSocketAddress();
plugin.getCore().getPlayerHandler().addPlayer(event.getPlayer().getName(), address.getHostString());
ProxiedPlayer player = event.getPlayer();
plugin.getCore().getPlayerHandler().addPlayer(address.getHostString(), player.getName(), player.getUniqueId());
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -26,21 +26,12 @@
package ch.andre601.advancedserverlist.bungeecord.events;
import ch.andre601.advancedserverlist.bungeecord.BungeeCordCore;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import ch.andre601.advancedserverlist.core.profiles.ProfileManager;
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.chat.TextComponent;
import ch.andre601.advancedserverlist.core.events.PingEventHandler;
import net.md_5.bungee.api.event.ProxyPingEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.event.EventPriority;
import java.net.InetSocketAddress;
import java.util.Map;
public class PingEvent implements Listener{
private final BungeeCordCore plugin;
@@ -52,55 +43,6 @@ public class PingEvent implements Listener{
@EventHandler(priority = EventPriority.LOW)
public void onProxyPing(ProxyPingEvent event){
ServerPing ping = event.getResponse();
ServerPing.Protocol protocol = ping.getVersion();
if(protocol == null)
return;
InetSocketAddress address = (InetSocketAddress)event.getConnection().getSocketAddress();
Map<String, Object> replacements = plugin.getCore()
.loadPlaceholders(protocol.getProtocol(), ping.getPlayers().getOnline(), ping.getPlayers().getMax(), address);
ServerListProfile profile = ProfileManager.get(plugin.getCore())
.replacements(replacements)
.getProfile();
if(profile == null)
return;
if(!profile.getMotd().isEmpty()){
TextComponent component = new TextComponent(BungeeComponentSerializer.get().serialize(
ComponentParser.list(profile.getMotd())
.replacements(replacements)
.toComponent()
));
ping.setDescriptionComponent(component);
}
if(!profile.getPlayerCount().isEmpty()){
protocol.setName(ComponentParser.text(profile.getPlayerCount())
.replacements(replacements)
.toString()
);
protocol.setProtocol(-1);
}
if(!profile.getPlayers().isEmpty()){
String players = ComponentParser.list(profile.getPlayers())
.replacements(replacements)
.toString();
ServerPing.PlayerInfo[] playerInfos = AdvancedServerList.getPlayers(ServerPing.PlayerInfo.class, players)
.toArray(new ServerPing.PlayerInfo[0]);
if(playerInfos.length > 0)
ping.getPlayers().setSample(playerInfos);
}
ping.setFavicon(ping.getFaviconObject());
ping.setVersion(protocol);
event.setResponse(ping);
PingEventHandler.handleEvent(new BungeeEventWrapper(plugin, event));
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -39,17 +39,22 @@ public class BungeeLogger implements PluginLogger{
}
@Override
public void info(String msg){
logger.info(msg);
public void info(String msg, Object... args){
logger.info(String.format(msg, args));
}
@Override
public void warn(String msg){
logger.warning(msg);
public void warn(String msg, Object... args){
logger.warning(String.format(msg, args));
}
@Override
public void warn(String msg, Throwable throwable){
logger.log(Level.WARNING, msg, throwable);
}
@Override
public void warn(String msg, Throwable throwable, Object... args){
logger.log(Level.WARNING, String.format(msg, args), throwable);
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -23,27 +23,16 @@
*
*/
package ch.andre601.advancedserverlist.core.interfaces;
package ch.andre601.advancedserverlist.bungeecord.objects;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.core.objects.CachedPlayer;
import java.nio.file.Path;
public interface PluginCore{
public class BungeePlayer extends GenericPlayer{
void loadCommands();
void loadEvents();
void loadMetrics();
AdvancedServerList getCore();
Path getPath();
PluginLogger getPluginLogger();
String getPlatformName();
String getPlatformVersion();
public BungeePlayer(CachedPlayer player, int protocol){
this.name = player.getName();
this.protocol = protocol;
this.uuid = player.getUuid();
}
}
@@ -0,0 +1,49 @@
/*
* 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.objects;
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 PlayerPlaceholders extends PlaceholderProvider{
public PlayerPlaceholders(){
super("player");
}
@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;
};
}
}
+60 -7
View File
@@ -2,7 +2,7 @@
<!--
~ MIT License
~
~ Copyright (c) 2022 Andre_601
~ 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
@@ -43,31 +43,78 @@
<maven.compiler.target>16</maven.compiler.target>
</properties>
<repositories>
<repository>
<id>flexver-repo</id>
<url>https://repo.sleeping.town/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>ch.andre601.advancedserverlist</groupId>
<artifactId>api</artifactId>
<version>${plugin.version}</version>
</dependency>
<dependency>
<groupId>org.spongepowered</groupId>
<artifactId>configurate-yaml</artifactId>
<version>${plugin.depend.configurate}</version>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-api</artifactId>
<version>${plugin.depend.kyori-adventure}</version>
<version>4.12.0</version>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-minimessage</artifactId>
<version>${plugin.depend.kyori-minimessage}</version>
<version>4.12.0</version>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-legacy</artifactId>
<version>${plugin.depend.kyori-legacy}</version>
<version>4.12.0</version>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.10.0</version>
</dependency>
<dependency>
<groupId>com.unascribed</groupId>
<artifactId>flexver-java</artifactId>
<version>1.1.0</version>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>AdvancedServerList-Core-${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>
@@ -77,9 +124,9 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<version>3.4.1</version>
<configuration>
<minimizeJar>true</minimizeJar>
<createDependencyReducedPom>false</createDependencyReducedPom>
<filters>
<filter>
<artifact>*:*</artifact>
@@ -89,6 +136,12 @@
</excludes>
</filter>
</filters>
<relocations>
<relocation>
<pattern>com.unascribed</pattern>
<shadedPattern>ch.andre601.advancedserverlist.core.depends.flexver</shadedPattern>
</relocation>
</relocations>
</configuration>
<executions>
<execution>
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -25,80 +25,45 @@
package ch.andre601.advancedserverlist.core;
import ch.andre601.advancedserverlist.api.AdvancedServerListAPI;
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
import ch.andre601.advancedserverlist.api.internal.ServerPlaceholders;
import ch.andre601.advancedserverlist.core.check.UpdateChecker;
import ch.andre601.advancedserverlist.core.commands.CommandHandler;
import ch.andre601.advancedserverlist.core.file.FileHandler;
import ch.andre601.advancedserverlist.core.interfaces.PluginCore;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
import ch.andre601.advancedserverlist.core.profiles.players.PlayerHandler;
import ch.andre601.advancedserverlist.core.profiles.replacer.Placeholders;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.InetSocketAddress;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.*;
import java.util.Properties;
public class AdvancedServerList{
private final PluginCore plugin;
private final PluginCore<?, ?, ?> plugin;
private final FileHandler fileHandler;
private final CommandHandler commandHandler;
private final PlayerHandler playerHandler;
private final AdvancedServerListAPI api = AdvancedServerListAPI.get();
public AdvancedServerList(PluginCore plugin){
private UpdateChecker updateChecker;
private String version;
public AdvancedServerList(PluginCore<?, ?, ?> plugin, PlaceholderProvider player){
this.plugin = plugin;
this.fileHandler = new FileHandler(this);
this.commandHandler = new CommandHandler(this);
this.playerHandler = new PlayerHandler(this);
this.api.addPlaceholderProvider(player);
this.api.addPlaceholderProvider(new ServerPlaceholders());
load();
}
public static <T> List<T> getPlayers(Class<T> clazz, String text){
try{
String[] lines = text.split("\n");
final List<T> players = new ArrayList<>(lines.length);
final Constructor<T> constructor = clazz.getDeclaredConstructor(String.class, UUID.class);
constructor.setAccessible(true);
for(String line : lines){
players.add(constructor.newInstance(
ComponentParser.text(line).toString(), UUID.randomUUID()
));
}
return players;
}catch(NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException ex){
return Collections.emptyList();
}
}
public Map<String, Object> loadPlaceholders(int protocol, int online, int max, InetSocketAddress ip){
Map<String, Object> replacements = new HashMap<>();
replacements.put(Placeholders.PLAYER_PROTOCOL, protocol);
replacements.put(Placeholders.PLAYERS_ONLINE, online);
replacements.put(Placeholders.PLAYERS_MAX, max);
replacements.put(Placeholders.PLAYER_NAME, getPlayerHandler().getPlayerByIp(ip.getHostString()));
return replacements;
}
public void disable(){
getPluginLogger().info("Saving cache.data file...");
getPlayerHandler().save();
getPluginLogger().info("AdvancedServerList disabled!");
}
public PluginLogger getPluginLogger(){
return plugin.getPluginLogger();
}
public Path getPath(){
return plugin.getPath();
public PluginCore<?, ?, ?> getPlugin(){
return plugin;
}
public FileHandler getFileHandler(){
@@ -113,48 +78,96 @@ public class AdvancedServerList{
return playerHandler;
}
public String getVersion(){
return version;
}
public AdvancedServerListAPI getApi(){
return api;
}
public void disable(){
getPlugin().getPluginLogger().info("Saving playercache.json file...");
getPlayerHandler().save();
if(updateChecker != null){
getPlugin().getPluginLogger().info("Disabling Update Checker...");
updateChecker.disable();
}
getPlugin().getPluginLogger().info("AdvancedServerList disabled!");
}
public void clearFaviconCache(){
plugin.clearFaviconCache();
}
private void load(){
printBanner();
getPluginLogger().info("Starting AdvancedServerList...");
getPluginLogger().info("Proxy: " + plugin.getPlatformName() + " " + plugin.getPlatformVersion());
resolveVersion();
getPlugin().getPluginLogger().info("Starting AdvancedServerList v%s...", version);
getPlugin().getPluginLogger().info("Platform: " + plugin.getPlatformName() + " " + plugin.getPlatformVersion());
if(getFileHandler().loadConfig()){
getPluginLogger().info("Successfully loaded config.yml!");
getPlugin().getPluginLogger().info("Successfully loaded config.yml!");
}else{
getPluginLogger().warn("Unable to load config.yml! Check previous lines for errors.");
getPlugin().getPluginLogger().warn("Unable to load config.yml! Check previous lines for errors.");
return;
}
if(getFileHandler().loadProfiles()){
getPluginLogger().info("Successfully loaded " + getFileHandler().getProfiles().size() + " profiles!");
getPlugin().getPluginLogger().info("Successfully loaded " + getFileHandler().getProfiles().size() + " profiles!");
}else{
getPluginLogger().warn("Unable to load profiles! Check previous lines for errors.");
getPlugin().getPluginLogger().warn("Unable to load profiles! Check previous lines for errors.");
return;
}
getPluginLogger().info("Loading Commands...");
Path folder = getPlugin().getFolderPath().resolve("favicons");
if(!folder.toFile().exists() && folder.toFile().mkdirs())
getPlugin().getPluginLogger().info("Successfully created favicons folder.");
getPlugin().getPluginLogger().info("Loading Commands...");
plugin.loadCommands();
getPluginLogger().info("Commands loaded!");
getPluginLogger().info("Loading events...");
getPlugin().getPluginLogger().info("Commands loaded!");
getPlugin().getPluginLogger().info("Loading events...");
plugin.loadEvents();
getPluginLogger().info("Events loaded!");
getPluginLogger().info("Loading cache.data...");
getPlugin().getPluginLogger().info("Events loaded!");
getPlugin().getPluginLogger().info("Loading playercache.json...");
getPlayerHandler().load();
getPlugin().getPluginLogger().info("Loading bStats metrics. Disable it in the global config under /plugins/bstats/");
plugin.loadMetrics();
getPlugin().getPluginLogger().info("Metrics loaded!");
getPlugin().getPluginLogger().info("AdvancedServerList is ready!");
getPluginLogger().info("AdvancedServerList is ready!");
if(getFileHandler().getBoolean("check_updates"))
this.updateChecker = new UpdateChecker(this);
}
private void printBanner(){
getPluginLogger().info("");
getPluginLogger().info(" _____ _");
getPluginLogger().info(" /\\ / ____| |");
getPluginLogger().info(" / \\ | (___ | |");
getPluginLogger().info(" / /\\ \\ \\___ \\| |");
getPluginLogger().info(" / ____ \\ ____) | |____");
getPluginLogger().info("/_/ \\_\\_____/|______|");
getPluginLogger().info("");
getPlugin().getPluginLogger().info("");
getPlugin().getPluginLogger().info(" _____ _");
getPlugin().getPluginLogger().info(" /\\ / ____| |");
getPlugin().getPluginLogger().info(" / \\ | (___ | |");
getPlugin().getPluginLogger().info(" / /\\ \\ \\___ \\| |");
getPlugin().getPluginLogger().info(" / ____ \\ ____) | |____");
getPlugin().getPluginLogger().info("/_/ \\_\\_____/|______|");
getPlugin().getPluginLogger().info("");
}
private void resolveVersion(){
try(InputStream is = getClass().getResourceAsStream("/version.properties")){
Properties properties = new Properties();
properties.load(is);
version = properties.getProperty("version");
}catch(IOException ex){
version = "UNKNOWN";
}
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -23,19 +23,15 @@
*
*/
package ch.andre601.advancedserverlist.core.profiles.replacer;
package ch.andre601.advancedserverlist.core.check;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Map;
import org.jetbrains.annotations.NotNull;
/*
* Original by Imo van den Berge (aka Bergerkiller: https://github.com/Bergerkiller)
*
* Original Source: https://github.com/bergerhealer/BKCommonLib/blob/master/src/main/java/com/bergerkiller/bukkit/common/collections/EntryList.java
*/
public class EntryList<K, V> extends ArrayList<Map.Entry<K, V>>{
public void add(K key, V value){
add(new AbstractMap.SimpleEntry<>(key, value));
import java.util.concurrent.ThreadFactory;
public class UpdateCheckThread implements ThreadFactory{
@Override
public Thread newThread(@NotNull Runnable r){
return new Thread(r, "AdvancedServerList Update-Thread");
}
}
@@ -0,0 +1,223 @@
/*
* 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.core.check;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.unascribed.flexver.FlexVerComparator;
import io.leangen.geantyref.TypeToken;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class UpdateChecker{
private final String url = "https://api.modrinth.com/v2/project/advancedserverlist/version?loaders=[\"%s\"]";
private final OkHttpClient client = new OkHttpClient();
private final AdvancedServerList core;
private final PluginLogger logger;
private final String loader;
private final Type listType = new TypeToken<ArrayList<ModrinthVersion>>(){}.getType();
private final Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.setPrettyPrinting()
.setLenient()
.create();
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(new UpdateCheckThread());
public UpdateChecker(AdvancedServerList core){
this.core = core;
this.logger = core.getPlugin().getPluginLogger();
this.loader = core.getPlugin().getLoader();
startUpdateChecker();
}
public void startUpdateChecker(){
executor.scheduleAtFixedRate(() -> {
logger.info("Checking for a new update...");
checkUpdate().whenComplete((version, throwable) -> {
if(version == null || throwable != null){
logger.warn("Failed to look for any updates. See previous messages for reasons.");
return;
}
int result = version.compare(core.getVersion());
switch(result){
case -1 -> printUpdateBanner(version.getVersionNumber(), version.getId(), version.isRelease());
case 0 -> logger.info("No new update found. You're running the latest version!");
case 1 -> logger.info(
"Your version (%s) is higher than the latest release (%s). Are you running a dev build?",
core.getVersion(), version.getVersionNumber()
);
}
});
}, 0L, 12L, TimeUnit.HOURS);
}
public void disable(){
executor.shutdown();
client.dispatcher().executorService().shutdown();
try{
if(!executor.awaitTermination(1, TimeUnit.SECONDS)){
executor.shutdownNow();
if(!executor.awaitTermination(1, TimeUnit.SECONDS))
logger.warn("Scheduler didn't terminate in time!");
}
if(!client.dispatcher().executorService().awaitTermination(1, TimeUnit.SECONDS)){
client.dispatcher().executorService().shutdownNow();
if(!client.dispatcher().executorService().awaitTermination(1, TimeUnit.SECONDS))
logger.warn("OkHttp's Scheduler didn't terminate in time!");
}
}catch(InterruptedException ex){
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
private CompletableFuture<ModrinthVersion> checkUpdate(){
return CompletableFuture.supplyAsync(() -> {
PluginLogger logger = core.getPlugin().getPluginLogger();
if(core.getVersion().equals("UNKNOWN")){
logger.warn("Cannot perform Update check! Plugin version couldn't be parsed.");
return null;
}
Request request = new Request.Builder()
.url(String.format(url, loader))
.header("User-Agent", "AdvancedServerList-" + loader + "/" + core.getVersion())
.build();
try(Response response = client.newCall(request).execute()){
if(!response.isSuccessful()){
logger.warn("Encountered a non-successful response from Modrinth! Code: %d", response.code());
return null;
}
ResponseBody body = response.body();
if(body == null){
logger.warn("Received an empty Response body from Modrinth!");
return null;
}
String responseString = body.string();
if(responseString.isEmpty()){
logger.warn("Received an empty Response body from Modrinth!");
return null;
}
List<ModrinthVersion> list = gson.fromJson(responseString, listType);
if(list == null || list.isEmpty()){
logger.warn("Couldn't convert JSON Array into a valid list.");
return null;
}
ModrinthVersion version = list.get(0);
if(version.getVersionNumber() == null || version.getVersionNumber().isEmpty()){
logger.warn("Cannot check latest version. Received version number was null/empty.");
return null;
}
return version;
}catch(JsonSyntaxException ex){
logger.warn("Encountered invalid JSON from Response body!", ex);
return null;
}catch(IOException ex){
logger.warn("Encountered Exception while checking for an update!", ex);
return null;
}
});
}
private void printUpdateBanner(String version, String versionId, boolean isRelease){
logger.info("==================================================================");
logger.info("You are running an outdated version of AdvancedServerList!");
logger.info("");
logger.info("Your version: %s", core.getVersion());
logger.info("Modrinth version: %s", version);
logger.info("");
if(!isRelease){
logger.info("WARNING: This release is an Alpha/Beta! It may contain");
logger.info(" breaking changes!");
logger.info("");
}
logger.info("You can download the latest release from here:");
logger.info("https://modrinth.com/plugin/advancedserverlist/version/%s", versionId);
logger.info("==================================================================");
}
public static class ModrinthVersion{
@SuppressWarnings("FieldMayBeFinal")
private String id;
@SuppressWarnings("FieldMayBeFinal")
private String versionNumber;
@SuppressWarnings("FieldMayBeFinal")
private String versionType;
public ModrinthVersion(String id, String versionNumber, String versionType){
this.id = id;
this.versionNumber = versionNumber;
this.versionType = versionType;
}
public String getId(){
return id;
}
public String getVersionNumber(){
return versionNumber;
}
public boolean isRelease(){
return versionType.equals("release");
}
public int compare(String version){
return FlexVerComparator.compare(version, versionNumber);
}
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -37,37 +37,45 @@ public class CommandHandler{
}
public void handle(CmdSender sender, String[] args){
String errorPrefix = "<grey>[<gradient:dark_red:red>AdvancedServerList</gradient>]";
String prefix = "<grey>[<gradient:aqua:white>AdvancedServerList</gradient>]";
if(args.length == 0 || args[0].equalsIgnoreCase("help")){
if(sender.hasPermission("advancedserverlist.command.help")){
sender.sendMsg("AdvancedServerList Commands");
sender.sendMsg("%s - Commands", prefix);
sender.sendMsg();
sender.sendMsg("<aqua>/asl help <grey>- Shows this help.");
sender.sendMsg("<gradient:aqua:white>/asl help</gradient> <grey>- Shows this Help");
sender.sendMsg();
sender.sendMsg("<aqua>/asl reload <grey>- Reloads the config and profiles.");
sender.sendMsg("<gradient:aqua:white>/asl reload</gradient> <grey>- Reloads the plugin");
}else{
sender.sendMsg("<red>You do not have the permissions to execute this command!");
sender.sendMsg("%s <red>You do not have permissions to execute this command!", errorPrefix);
}
}else
if(args[0].equalsIgnoreCase("reload")){
if(sender.hasPermission("advancedserverlist.command.reload")){
sender.sendMsg("<grey>Reloading config.yml...");
sender.sendMsg("%s Reloading plugin...", prefix);
if(core.getFileHandler().reloadConfig()){
sender.sendMsg("<green>Successfully reloaded <grey>config.yml</grey>!");
sender.sendMsg("%s <green>Reloaded <grey>config.yml</grey>!", prefix);
}else{
sender.sendMsg("<red>Error while reloading the config.yml.");
sender.sendMsg("%s <red>Error while reloading the <grey>config.yml</grey>!", errorPrefix);
}
sender.sendMsg("<grey>Reloading Profiles...");
if(core.getFileHandler().reloadProfiles()){
sender.sendMsg("<green>Successfully loaded <grey>%d</grey> Profiles!", core.getFileHandler().getProfiles().size());
sender.sendMsg("%s <green>Loaded <grey>%d Profile(s)</grey>!", prefix, core.getFileHandler().getProfiles().size());
}else{
sender.sendMsg("<red>Error while reloading Profiles. No profiles have been loaded.");
sender.sendMsg("%s <red>Cannot load Profiles!", errorPrefix);
}
sender.sendMsg("<green>Reload complete!");
core.clearFaviconCache();
sender.sendMsg("%s <green>Successfully cleared Favicon cache!", prefix);
sender.sendMsg("%s <green>Reload complete!", prefix);
}else{
sender.sendMsg("%s <red>You do not have permissions to execute this command!", errorPrefix);
}
}else{
sender.sendMsg("<red>Unknown subcommand <grey>%s</grey>. Use <grey>/asl help</grey> for a list of commands.", args[0]);
sender.sendMsg("%s <red>Unknown subcommand <grey>%s</grey>. Use <grey>/asl help</grey> for a list of commands.", prefix, args[0]);
}
}
}
@@ -0,0 +1,121 @@
/*
* 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.core.events;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
import ch.andre601.advancedserverlist.core.interfaces.events.GenericEventWrapper;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
import ch.andre601.advancedserverlist.core.profiles.profile.ProfileEntry;
import ch.andre601.advancedserverlist.core.profiles.profile.ProfileManager;
import ch.andre601.advancedserverlist.core.profiles.replacer.StringReplacer;
public class PingEventHandler{
public static <F, PL, P extends GenericPlayer> void handleEvent(GenericEventWrapper<F, PL, P> event){
if(event.isInvalidProtocol())
return;
PluginCore<F, PL, P> plugin = event.getPlugin();
String host = event.getVirtualHost();
int online = event.getOnlinePlayers();
int max = event.getMaxPlayers();
P player = event.createPlayer(
plugin.getCore().getPlayerHandler().getCachedPlayer(event.getPlayerIP()),
event.getProtocolVersion()
);
GenericServer server = new GenericServer(online, max, host);
ServerListProfile profile = ProfileManager.get(plugin.getCore())
.applyReplacements(player, server)
.getProfile();
if(profile == null)
return;
ProfileEntry entry = ProfileManager.merge(profile);
if(entry.isExtraPlayersEnabled().getValue(false)){
max = online + (entry.getExtraPlayersCount() == null ? 0 : entry.getExtraPlayersCount());
event.setMaxPlayers(max);
}
server = new GenericServer(online, max, host);
if(!entry.getMOTD().isEmpty()){
event.setMotd(
ComponentParser.list(entry.getMOTD())
.applyReplacements(player, server)
.modifyText(text -> event.parsePAPIPlaceholders(text, player))
.toComponent()
);
}
boolean hidePlayers = entry.isHidePlayersEnabled().getValue(false);
if(hidePlayers){
event.hidePlayers();
}
if(!entry.getPlayerCountText().isEmpty() && !hidePlayers){
event.setPlayerCount(
ComponentParser.text(entry.getPlayerCountText())
.applyReplacements(player, server)
.modifyText(text -> event.parsePAPIPlaceholders(text, player))
.toString()
);
}
if(!entry.getPlayers().isEmpty() && !hidePlayers){
event.setPlayers(entry.getPlayers(), player, server);
}
if(!entry.getFavicon().isEmpty()){
String favicon = StringReplacer.replace(entry.getFavicon(), player, server);
F fav = plugin.getFaviconHandler().getFavicon(favicon, image -> {
try{
return event.createFavicon(image);
}catch(Exception ex){
plugin.getPluginLogger().warn("Encountered an Exception while creating Favicon!", ex);
return null;
}
});
if(fav == null){
event.setDefaultFavicon();
}else{
event.setFavicon(fav);
}
}
event.updateEvent();
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -28,11 +28,12 @@ package ch.andre601.advancedserverlist.core.file;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
import ch.andre601.advancedserverlist.core.profiles.profile.ProfileEntry;
import ch.andre601.advancedserverlist.core.profiles.profile.ProfileSerializer;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
@@ -43,8 +44,6 @@ import java.util.List;
public class FileHandler{
private final FilenameFilter profileFilter = (dir, name) -> name.endsWith(".yml");
private final AdvancedServerList plugin;
private final PluginLogger logger;
@@ -55,12 +54,12 @@ public class FileHandler{
private ConfigurationNode node = null;
public FileHandler(AdvancedServerList plugin){
this.plugin = plugin;
this.logger = plugin.getPluginLogger();
public FileHandler(AdvancedServerList core){
this.plugin = core;
this.logger = core.getPlugin().getPluginLogger();
this.config = plugin.getPath().resolve("config.yml");
this.profilesFolder = plugin.getPath().resolve("profiles");
this.config = core.getPlugin().getFolderPath().resolve("config.yml");
this.profilesFolder = core.getPlugin().getFolderPath().resolve("profiles");
}
public List<ServerListProfile> getProfiles(){
@@ -69,10 +68,16 @@ public class FileHandler{
public boolean loadConfig(){
logger.info("Loading config.yml...");
File folder = config.toFile().getParentFile();
if(!folder.exists() && !folder.mkdirs()){
logger.warn("Couldn't create folder for plugin. Is it missing Write permissions?");
return false;
}
if(!config.toFile().exists()){
try(InputStream stream = plugin.getClass().getResourceAsStream("/config.yml")){
if(stream == null){
logger.warn("Cannot retrieve config.yml from Plugin.");
logger.warn("Cannot retrieve config.yml from plugin.");
return false;
}
@@ -103,6 +108,7 @@ public class FileHandler{
return false;
}
}
return reloadProfiles();
}
@@ -113,7 +119,7 @@ public class FileHandler{
public boolean reloadProfiles(){
profiles.clear();
File[] files = profilesFolder.toFile().listFiles(profileFilter);
File[] files = profilesFolder.toFile().listFiles(((dir, name) -> name.endsWith(".yml")));
if(files == null || files.length == 0){
logger.warn("Cannot load files from profiles folder! No valid YAML files present.");
return false;
@@ -124,7 +130,7 @@ public class FileHandler{
if(tmp == null)
continue;
profiles.add(new ServerListProfile(tmp));
profiles.add(ServerListProfile.Builder.resolve(file.getName(), tmp, logger).build());
logger.info("Loaded " + file.getName());
}
@@ -139,6 +145,7 @@ public class FileHandler{
public ConfigurationNode getConfigurationNode(Path path){
YamlConfigurationLoader loader = YamlConfigurationLoader.builder()
.defaultOptions(opts -> opts.serializers(build -> build.register(ProfileEntry.class, ProfileSerializer.INSTANCE)))
.path(path)
.build();
@@ -153,4 +160,8 @@ public class FileHandler{
public String getString(String def, Object... path){
return node.node(path).getString(def);
}
public boolean getBoolean(Object... path){
return node.node(path).getBoolean();
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -25,8 +25,6 @@
package ch.andre601.advancedserverlist.core.interfaces;
import net.kyori.adventure.text.format.NamedTextColor;
public interface CmdSender{
boolean hasPermission(String permission);
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -27,9 +27,11 @@ package ch.andre601.advancedserverlist.core.interfaces;
public interface PluginLogger{
void info(String msg);
void info(String msg, Object... args);
void warn(String msg);
void warn(String msg, Object... args);
void warn(String msg, Throwable throwable);
void warn(String msg, Throwable throwable, Object... args);
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -23,44 +23,40 @@
*
*/
package ch.andre601.advancedserverlist.core.profiles;
package ch.andre601.advancedserverlist.core.interfaces.core;
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.replacer.StringReplacer;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler;
import java.util.HashMap;
import java.util.Map;
import java.nio.file.Path;
import java.util.List;
public class ProfileManager{
public interface PluginCore<F, PL, P extends GenericPlayer>{
private final AdvancedServerList core;
void loadCommands();
private Map<String, Object> replacements = new HashMap<>();
void loadEvents();
private ProfileManager(AdvancedServerList core){
this.core = core;
}
void loadMetrics();
public static ProfileManager get(AdvancedServerList core){
return new ProfileManager(core);
}
void clearFaviconCache();
public ProfileManager replacements(Map<String, Object> replacements){
this.replacements = replacements;
return this;
}
AdvancedServerList getCore();
public ServerListProfile getProfile(){
for(ServerListProfile profile : core.getFileHandler().getProfiles()){
if(profile.getMotd().isEmpty() && profile.getPlayers().isEmpty() && profile.getPlayerCount().isEmpty())
continue;
Path getFolderPath();
ConditionsHolder conditions = profile.getConditions();
replacements.forEach(conditions::replace);
if(conditions.eval(core.getPluginLogger()))
return profile;
}
return null;
}
PluginLogger getPluginLogger();
FaviconHandler<F> getFaviconHandler();
String getPlatformName();
String getPlatformVersion();
String getLoader();
List<PL> createPlayers(List<String> lines, P player, GenericServer server);
}
@@ -0,0 +1,79 @@
/*
* 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.core.interfaces.events;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
import ch.andre601.advancedserverlist.core.objects.CachedPlayer;
import net.kyori.adventure.text.Component;
import java.awt.image.BufferedImage;
import java.net.InetSocketAddress;
import java.util.List;
public interface GenericEventWrapper<F, PL, P extends GenericPlayer>{
void setMaxPlayers(int maxPlayers);
void setMotd(Component component);
void hidePlayers();
void setPlayerCount(String name);
void setPlayers(List<String> players, P player, GenericServer server);
void setFavicon(F favicon);
void setDefaultFavicon();
void updateEvent();
boolean isInvalidProtocol();
int getProtocolVersion();
int getOnlinePlayers();
int getMaxPlayers();
String getPlayerIP();
String parsePAPIPlaceholders(String text, P player);
String getVirtualHost();
PluginCore<F, PL, P> getPlugin();
P createPlayer(CachedPlayer player, int protocol);
F createFavicon(BufferedImage image) throws Exception;
default String resolveHost(InetSocketAddress address){
return address == null ? null : address.getHostString();
}
}
@@ -0,0 +1,54 @@
/*
* 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.core.objects;
import java.util.UUID;
@SuppressWarnings("FieldMayBeFinal")
public class CachedPlayer{
private String ip;
private String name;
private UUID uuid;
public CachedPlayer(String ip, String name, UUID uuid){
this.ip = ip;
this.name = name;
this.uuid = uuid;
}
public String getIp(){
return ip;
}
public String getName(){
return name;
}
public UUID getUuid(){
return uuid;
}
}
@@ -0,0 +1,53 @@
/*
* 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.core.objects;
/*
* A simple class to allow having a nullable boolean without having to deal with possible NPEs
* when trying to get Boolean.getValue() while it is null...
*
* Here, it simply checks if the value is null and if it is, returns a default. Otherwise, it gives
* the value.
*/
public class NullBool{
public static final NullBool FALSE = new NullBool(false);
public static final NullBool NULL = new NullBool(null);
private final Boolean value;
public NullBool(Boolean value){
this.value = value;
}
public boolean isNull(){
return value == null;
}
public boolean getValue(boolean def){
return isNull() ? def : value;
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -25,25 +25,24 @@
package ch.andre601.advancedserverlist.core.parsing;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.core.profiles.replacer.StringReplacer;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
public class ComponentParser{
private static final MiniMessage mm;
private static final LegacyComponentSerializer legacy;
private static final StringReplacer replacer;
static {
mm = MiniMessage.miniMessage();
legacy = LegacyComponentSerializer.legacySection();
replacer = new StringReplacer();
}
private String text;
@@ -60,18 +59,18 @@ public class ComponentParser{
return new ComponentParser(String.join("\n", lines));
}
public ComponentParser replacements(Map<String, Object> replacements){
replacements.forEach(replacer::add);
public ComponentParser applyReplacements(GenericPlayer player, GenericServer server){
text = StringReplacer.replace(text, player, server);
return this;
}
public ComponentParser function(Function<String, String> function){
public ComponentParser modifyText(Function<String, String> function){
this.text = function.apply(text);
return this;
}
public Component toComponent(){
return mm.deserialize(replacer.replace(text));
return mm.deserialize(text);
}
@Override
@@ -1,139 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2022 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;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.profiles.replacer.StringReplacer;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class ConditionsHolder{
private final List<String> expressions = new ArrayList<>();
private final StringReplacer replacer = new StringReplacer();
public ConditionsHolder(List<String> expressions){
if(expressions != null && !expressions.isEmpty())
this.expressions.addAll(expressions);
}
public void replace(String from, Object to){
replacer.add(from, to);
}
public boolean eval(PluginLogger logger){
if(expressions.isEmpty())
return true;
for(String expression : expressions){
if(!parseExpression(expression, logger))
return false;
}
return true;
}
private boolean parseExpression(String expression, PluginLogger logger){
if(expression == null || expression.isEmpty())
return true;
String newExpression = replacer.replace(expression);
char[] chars = newExpression.toCharArray();
StringBuilder left = new StringBuilder();
StringBuilder operator = new StringBuilder();
StringBuilder right = new StringBuilder();
boolean operatorFound = false;
for(int i = 0; i < chars.length; i++){
char c = chars[i];
if(c == ' ')
continue;
if((c != '<' && c != '>' && c != '=' && c != '!') || i + 1 >= chars.length){
if(operatorFound){
right.append(c);
continue;
}
left.append(c);
continue;
}
if(operatorFound){
logger.warn("Encountered second operator in condition! AdvancedServerList currently only supports one operator!");
return false;
}
operatorFound = true;
char next = chars[i + 1];
if(c == '!' && next != '='){
logger.warn("Invalid condition found. Expected '!=' but found '!" + next + "' in condition " + newExpression);
return false;
}
operator.append(c);
i++;
if(c == '!' || ((c == '<' || c == '>') && next == '=')){
operator.append(next);
}
}
if(left.isEmpty() || operator.isEmpty() || right.isEmpty()){
logger.warn("Failed to evaluate condition. One part of the expression was empty!");
return false;
}
int leftInt = tryParse(left.toString());
int rightInt = tryParse(right.toString());
return switch(operator.toString().toLowerCase(Locale.ROOT)){
case ">" -> leftInt > rightInt;
case ">=" -> leftInt >= rightInt;
case "=" -> left.toString().equals(right.toString());
case "!=" -> !left.toString().equals(right.toString());
case "<" -> leftInt < rightInt;
case "<=" -> leftInt <= rightInt;
default -> false;
};
}
private int tryParse(String value){
try{
return Integer.parseInt(value);
}catch(NumberFormatException ex){
return value.length();
}
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -25,64 +25,165 @@
package ch.andre601.advancedserverlist.core.profiles;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.profiles.conditions.Expression;
import ch.andre601.advancedserverlist.core.profiles.profile.ProfileEntry;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.serialize.SerializationException;
import java.util.Collections;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class ServerListProfile{
private final ConditionsHolder conditions;
private final int priority;
private final List<Expression> expressions;
private final ProfileEntry defaultProfile;
private final List<String> motd;
private final List<String> players;
private final String playerCount;
private final List<ProfileEntry> profiles;
public ServerListProfile(ConfigurationNode node){
this.conditions = new ConditionsHolder(getList(node, "conditions", false));
this.priority = node.node("priority").getInt();
this.motd = getList(node, "motd", true);
this.players = getList(node, "players", false);
this.playerCount = node.node("playerCount").getString("");
}
private final Random random = new Random();
public ConditionsHolder getConditions(){
return conditions;
public ServerListProfile(int priority, List<Expression> expressions, List<ProfileEntry> profiles, ProfileEntry defaultProfile){
this.priority = priority;
this.expressions = expressions;
this.profiles = profiles;
this.defaultProfile = defaultProfile;
}
public int getPriority(){
return priority;
}
public List<String> getMotd(){
return motd;
}
public List<String> getPlayers(){
return players;
}
public String getPlayerCount(){
return playerCount;
}
private List<String> getList(ConfigurationNode node, String key, boolean trim){
List<String> list;
try{
list = node.node(key).getList(String.class);
}catch(SerializationException ex){
return Collections.emptyList();
public boolean evalConditions(GenericPlayer player, GenericServer server){
if(expressions.isEmpty())
return true;
for(Expression expression : expressions){
if(!expression.evaluate(player, server))
return false;
}
if(list == null)
return Collections.emptyList();
return true;
}
public ProfileEntry getRandomProfile(){
if(profiles.isEmpty()){
return null;
}
if(trim && list.size() > 2)
return list.subList(0, 2);
if(profiles.size() == 1)
return profiles.get(0); // No need to run a random for 1 profile.
return list;
synchronized(random){
return profiles.get(random.nextInt(profiles.size()));
}
}
public ProfileEntry getDefaultProfile(){
return defaultProfile;
}
/*
* Returns true if the Profile...
* ...doesn't have any valid MOTD set AND
* ...doesn't have any player hover set AND
* ...doesn't have a player count text set and hidePlayers is false AND
* ...doesn't have a favicon set.
*/
public boolean isInvalidProfile(){
if(profiles.isEmpty())
return defaultProfile.isInvalidProfile();
boolean profilesValid = false;
for(ProfileEntry profile : profiles){
if(profile.isInvalidProfile())
continue;
profilesValid = true;
break;
}
return !profilesValid && defaultProfile.isInvalidProfile();
}
public static class Builder{
private final String fileName;
private final ConfigurationNode node;
private final int priority;
private final List<Expression> expressions = new ArrayList<>();
private final PluginLogger logger;
private List<ProfileEntry> profiles = new ArrayList<>();
private ProfileEntry defaultProfile = ProfileEntry.empty();
private Builder(String fileName, ConfigurationNode node, PluginLogger logger){
this.fileName = fileName;
this.node = node;
this.priority = node.node("priority").getInt();
this.logger = logger;
}
public static Builder resolve(String fileName, ConfigurationNode node, PluginLogger logger){
return new Builder(fileName, node, logger)
.resolveExpressions()
.resolveProfiles()
.resolveDefaultProfile();
}
private Builder resolveExpressions(){
List<String> temp;
try{
temp = node.node("conditions").getList(String.class);
}catch(SerializationException ex){
logger.warn("Encountered a SerializationException while resolving conditions for %s", ex, fileName);
return this;
}
if(temp == null || temp.isEmpty())
return this;
for(String str : temp){
Expression expr = new Expression(str);
if(expr.getResult() != Expression.ExpressionResult.VALID){
logger.warn("Found invalid expression in condition '%s'! Reason: %s", str, expr.getResult().getMessage());
continue;
}
this.expressions.add(expr);
}
return this;
}
private Builder resolveProfiles(){
try{
this.profiles = node.node("profiles").getList(ProfileEntry.class);
}catch(SerializationException ex){
logger.warn("Encountered a SerializationException while resolving the profiles entry for %s", ex, fileName);
}
return this;
}
private Builder resolveDefaultProfile(){
try{
this.defaultProfile = node.get(ProfileEntry.class);
}catch(SerializationException ex){
logger.warn("Encountered a SerializationException while resolving the global profile for %s", ex, fileName);
}
return this;
}
public ServerListProfile build(){
return new ServerListProfile(this.priority, this.expressions, this.profiles, this.defaultProfile);
}
}
}
@@ -0,0 +1,208 @@
/*
* 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.core.profiles.conditions;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.core.profiles.replacer.StringReplacer;
public class Expression{
private ExpressionResult result = ExpressionResult.VALID;
private String left;
private Operator operator = Operator.UNKNOWN;
private String right;
public Expression(String expression){
resolveExpression(expression);
}
public boolean evaluate(GenericPlayer player, GenericServer server){
String newLeft = StringReplacer.replace(left, player, server);
String newRight = StringReplacer.replace(right, player, server);
return operator.evaluate(newLeft, newRight);
}
public ExpressionResult getResult(){
return result;
}
private void resolveExpression(String expression){
if(expression == null || expression.isEmpty()){
result = ExpressionResult.INVALID_NO_EXPRESSION;
return;
}
char[] chars = expression.toCharArray();
StringBuilder left = new StringBuilder();
StringBuilder right = new StringBuilder();
boolean foundOperator = false;
for(int i = 0; i < chars.length; i++){
char c = chars[i];
char next = (i == (chars.length - 1)) ? '\0' : chars[i + 1];
Operator tmp = Operator.getOperand(c, next);
if(tmp != Operator.UNKNOWN && !foundOperator){
operator = tmp;
foundOperator = true;
if(tmp.hasSecond())
i++; // Skip next character if operator has 2
continue;
}
if(foundOperator){
right.append(c);
}else{
left.append(c);
}
}
if(left.isEmpty() || right.isEmpty()){
result = ExpressionResult.INVALID_EMPTY_PARTS;
return;
}
this.left = left.toString();
this.right = right.toString();
}
public enum Operator{
LESS('<', '\0'){
@Override
public boolean evaluate(String left, String right){
int leftInt = Operator.getInt(left);
int rightInt = Operator.getInt(right);
return leftInt < rightInt;
}
},
LESS_OR_EQUAL('<', '='){
@Override
public boolean evaluate(String left, String right){
int leftInt = Operator.getInt(left);
int rightInt = Operator.getInt(right);
return leftInt <= rightInt;
}
},
EQUAL('=', '\0'){
@Override
public boolean evaluate(String left, String right){
return left.equals(right);
}
},
NOT_EQUAL('!', '='){
@Override
public boolean evaluate(String left, String right){
return !left.equals(right);
}
},
GREATER('>', '\0'){
@Override
public boolean evaluate(String left, String right){
int leftInt = Operator.getInt(left);
int rightInt = Operator.getInt(right);
return leftInt > rightInt;
}
},
GREATER_OR_EQUAL('>', '='){
@Override
public boolean evaluate(String left, String right){
int leftInt = Operator.getInt(left);
int rightInt = Operator.getInt(right);
return leftInt >= rightInt;
}
},
UNKNOWN('\0', '\0'){
@Override
public boolean evaluate(String left, String right){
return false;
}
};
private static final Operator[] VALUES = values();
private final char first;
private final char second;
Operator(char first, char second){
this.first = first;
this.second = second;
}
public static Operator getOperand(char first, char second){
for(Operator operator : VALUES){
if(operator.first == first){
if((!operator.hasSecond() && second != '=') || operator.second == second)
return operator;
}
}
return Operator.UNKNOWN;
}
public boolean hasSecond(){
return second != '\0';
}
public abstract boolean evaluate(String left, String right);
private static int getInt(String text){
try{
return Integer.parseInt(text);
}catch(NumberFormatException ex){
return text.length();
}
}
}
public enum ExpressionResult{
VALID(null),
INVALID_NO_EXPRESSION("Received empty condition which isn't allowed!"),
INVALID_EMPTY_PARTS ("Received condition with either left or right part being empty.");
private final String message;
ExpressionResult(String message){
this.message = message;
}
public String getMessage(){
return message;
}
}
}
@@ -0,0 +1,138 @@
/*
* 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.core.profiles.favicon;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
public class FaviconHandler<F>{
private final Cache<String, F> favicons = Caffeine.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.build();
private final AdvancedServerList core;
public FaviconHandler(AdvancedServerList core){
this.core = core;
}
public F getFavicon(String input, Function<BufferedImage, F> function){
return favicons.get(input, k -> {
BufferedImage image = resolveImage(core, input);
if(image == null)
return null;
return function.apply(image);
});
}
public void clearCache(){
favicons.invalidateAll();
}
private BufferedImage resolveImage(AdvancedServerList core, String input){
InputStream stream;
if(input.toLowerCase(Locale.ROOT).startsWith("https://")){
stream = getFromUrl(core, input);
}else
if(input.toLowerCase(Locale.ROOT).endsWith(".png")){
File folder = core.getPlugin().getFolderPath().resolve("favicons").toFile();
if(!folder.exists()){
core.getPlugin().getPluginLogger().warn("Cannot get Favicon %s from favicons folder. Folder doesn't exist!", input);
return null;
}
File file = new File(folder, input);
try{
stream = new FileInputStream(file);
}catch(IOException ex){
core.getPlugin().getPluginLogger().warn("Cannot create Favicon from File %s.", input);
core.getPlugin().getPluginLogger().warn("Cause: %s", ex.getMessage());
return null;
}
}else{
stream = getFromUrl(core, "https://mc-heads.net/avatar/" + input + "/64");
}
if(stream == null){
core.getPlugin().getPluginLogger().warn("Cannot create Favicon. InputStream was null.");
return null;
}
try{
BufferedImage original = ImageIO.read(stream);
if(original == null){
core.getPlugin().getPluginLogger().warn("Cannot create Favicon. Unable to create BufferedImage.");
return null;
}
BufferedImage favicon = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB);
Graphics2D img = favicon.createGraphics();
// Resize original image to 64x64 and apply to favicon.
img.drawImage(original, 0, 0, 64, 64, null);
img.dispose();
return favicon;
}catch(IOException ex){
core.getPlugin().getPluginLogger().warn("Unable to create Favicon. Encountered IOException during creation.");
core.getPlugin().getPluginLogger().warn("Cause: %s", ex.getMessage());
return null;
}
}
private InputStream getFromUrl(AdvancedServerList core, String url){
try{
URL faviconUrl = new URL(url);
URLConnection connection = faviconUrl.openConnection();
connection.setRequestProperty("User-Agent", "AdvancedServerList/" + core.getVersion());
connection.connect();
return connection.getInputStream();
}catch(IOException ex){
core.getPlugin().getPluginLogger().warn("Error while connecting to %s for Favicon creation.", url);
core.getPlugin().getPluginLogger().warn("Cause: %s", ex.getMessage());
return null;
}
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -26,89 +26,135 @@
package ch.andre601.advancedserverlist.core.profiles.players;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.profiles.replacer.EntryList;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.objects.CachedPlayer;
import com.google.gson.Gson;
import io.leangen.geantyref.TypeToken;
import java.io.ByteArrayInputStream;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.UUID;
public class PlayerHandler{
private final AdvancedServerList core;
private final PluginLogger logger;
private final Path cache;
private final EntryList<String, String> players = new EntryList<>();
private List<CachedPlayer> cachedPlayers = new ArrayList<>();
private final Type listType = new TypeToken<ArrayList<CachedPlayer>>(){}.getType();
private final Gson gson = new Gson();
// UUID of MHF_Question
private final String defaultUUID = "606e2ff0-ed77-4842-9d6c-e1d3321c7838";
private CachedPlayer defaultPlayer = null;
public PlayerHandler(AdvancedServerList core){
this.core = core;
this.cache = core.getPath().resolve("cache.data");
this.logger = core.getPlugin().getPluginLogger();
this.cache = core.getPlugin().getFolderPath().resolve("playercache.json");
}
public void load(){
if(!cache.toFile().exists()){
core.getPluginLogger().info("No cache.data present. Skipping...");
logger.info("No playercache.json file present. Skipping...");
return;
}
List<String> lines;
if(core.getFileHandler().getBoolean("disable_cache")){
logger.info("'disable_cache' is set to true. Skipping playercache.json loading...");
return;
}
try{
lines = Files.readAllLines(cache);
}catch(IOException ex){
core.getPluginLogger().warn("Encountered IOException while trying to read cache.data", ex);
return;
}
if(lines.isEmpty()){
core.getPluginLogger().info("cache.data is empty. Skipping...");
return;
}
for(String line : lines){
String[] parts = line.split("=", 2);
if(parts.length < 2 || parts[0].isEmpty() || parts[1].isEmpty())
continue;
Reader reader = Files.newBufferedReader(cache);
players.add(parts[0], parts[1]);
cachedPlayers = gson.fromJson(reader, listType);
reader.close();
}catch(IOException ex){
logger.warn("Encountered IOException while reading the playercache.json file!", ex);
return;
}
core.getPluginLogger().info("Loaded " + players.size() + " players into cache!");
logger.info("Loaded %d players into cache!", cachedPlayers.size());
}
public void save(){
if(players.isEmpty()){
core.getPluginLogger().info("No data to save. Skipping...");
if(cachedPlayers.isEmpty()){
logger.info("No players in cache to save. Skipping...");
return;
}
StringJoiner joiner = new StringJoiner("\n");
for(Map.Entry<String, String> entry : players){
joiner.add(entry.getKey() + "=" + entry.getValue());
}
try(InputStream stream = new ByteArrayInputStream(joiner.toString().getBytes(StandardCharsets.UTF_8))){
Files.copy(stream, cache, StandardCopyOption.REPLACE_EXISTING);
core.getPluginLogger().info("Saved cache.data file.");
}catch(IOException ex){
core.getPluginLogger().warn("Cannot save player data to cache.data file!", ex);
}
}
public void addPlayer(String name, String ip){
players.add(name, ip);
}
public String getPlayerByIp(String ip){
for(Map.Entry<String, String> entry : players){
if(entry.getValue().equals(ip))
return entry.getKey();
if(core.getFileHandler().getBoolean("disable_cache")){
logger.info("'disable_cache' is set to true. Skipping saving of cached players...");
return;
}
return core.getFileHandler().getString("Anonymous", "unknown_player");
try(BufferedWriter writer = Files.newBufferedWriter(cache, StandardCharsets.UTF_8)){
gson.toJson(cachedPlayers, listType, writer);
logger.info("Successfully saved playercache.json file.");
}catch(IOException ex){
logger.warn("Encountered IOException while saving cached players to playercache.json!", ex);
}
}
public void addPlayer(String ip, String name, UUID uuid){
if(contains(ip) || core.getFileHandler().getBoolean("disable_cache"))
return;
cachedPlayers.add(new CachedPlayer(ip, name, uuid));
}
public CachedPlayer getCachedPlayer(String key){
if(!contains(key) || core.getFileHandler().getBoolean("disable_cache"))
return getDefaultPlayer();
for(CachedPlayer player : cachedPlayers){
if(player.getIp().equals(key))
return player;
}
return getDefaultPlayer();
}
private boolean contains(String ip){
for(CachedPlayer player : cachedPlayers){
if(player.getIp().equals(ip))
return true;
}
return false;
}
private CachedPlayer getDefaultPlayer(){
if(defaultPlayer != null)
return defaultPlayer;
return (defaultPlayer = new CachedPlayer(
"0.0.0.0",
core.getFileHandler().getString("Anonymous", "unknown_player"),
convertToUUID(core.getFileHandler().getString(defaultUUID, "unknown_player_uuid"))
));
}
private UUID convertToUUID(String uuid){
try{
return UUID.fromString(uuid);
}catch(IllegalArgumentException ex){
// This is always a valid UUID.
return UUID.fromString(defaultUUID);
}
}
}
@@ -0,0 +1,194 @@
/*
* 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.core.profiles.profile;
import ch.andre601.advancedserverlist.core.objects.NullBool;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.serialize.SerializationException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ProfileEntry{
private final List<String> motd;
private final List<String> players;
private final String playerCountText;
private final String favicon;
private final NullBool hidePlayersEnabled;
private final NullBool extraPlayersEnabled;
private final Integer extraPlayersCount;
public ProfileEntry(List<String> motd, List<String> players,
String playerCountText, String favicon, NullBool hidePlayersEnabled,
NullBool extraPlayersEnabled, Integer extraPlayersCount){
this.motd = motd;
this.players = players;
this.playerCountText = playerCountText;
this.favicon = favicon;
this.hidePlayersEnabled = hidePlayersEnabled;
this.extraPlayersEnabled = extraPlayersEnabled;
this.extraPlayersCount = extraPlayersCount;
}
public static ProfileEntry empty(){
return new ProfileEntry(Collections.emptyList(), Collections.emptyList(), "", "",
NullBool.FALSE, NullBool.FALSE, 0);
}
public List<String> getMOTD(){
return motd;
}
public List<String> getPlayers(){
return players;
}
public String getPlayerCountText(){
return playerCountText;
}
public String getFavicon(){
return favicon;
}
public NullBool isHidePlayersEnabled(){
return hidePlayersEnabled;
}
public NullBool isExtraPlayersEnabled(){
return extraPlayersEnabled;
}
public Integer getExtraPlayersCount(){
return extraPlayersCount;
}
public boolean isInvalidProfile(){
return getMOTD().isEmpty() &&
getPlayers().isEmpty() &&
(getPlayerCountText().isEmpty() && !isHidePlayersEnabled().getValue(false)) &&
getFavicon().isEmpty();
}
public static class Builder{
private final ConfigurationNode node;
private List<String> motd = new ArrayList<>();
private List<String> players = new ArrayList<>();
private String playerCountText = null;
private String favicon = null;
private NullBool hidePlayersEnabled = NullBool.FALSE;
private NullBool extraPlayersEnabled = NullBool.FALSE;
private Integer extraPlayersCount = 0;
private Builder(ConfigurationNode node){
this.node = node;
}
public static Builder resolve(ConfigurationNode node){
return new Builder(node)
.resolveMOTD()
.resolvePlayers()
.resolvePlayerCountText()
.resolveFavicon()
.resolveHidePlayersEnabled()
.resolveExtraPlayersEnabled()
.resolveExtraPlayersCount();
}
public Builder resolveMOTD(){
List<String> motd = resolveList(node, "motd");
if(motd.size() <= 2){
this.motd = motd;
return this;
}
this.motd = motd.subList(0, 2);
return this;
}
public Builder resolvePlayers(){
this.players = this.resolveList(node, "playerCount", "hover");
return this;
}
public Builder resolvePlayerCountText(){
this.playerCountText = node.node("playerCount", "text").getString("");
return this;
}
public Builder resolveFavicon(){
this.favicon = node.node("favicon").getString("");
return this;
}
public Builder resolveHidePlayersEnabled(){
if(node.node("playerCount", "hidePlayers").virtual()){
this.hidePlayersEnabled = NullBool.NULL;
return this;
}
this.hidePlayersEnabled = new NullBool(node.node("playerCount", "hidePlayers").getBoolean());
return this;
}
public Builder resolveExtraPlayersEnabled(){
if(node.node("playerCount", "extraPlayers", "enabled").virtual()){
this.extraPlayersEnabled = NullBool.NULL;
return this;
}
this.extraPlayersEnabled = new NullBool(node.node("playerCount", "extraPlayers", "enabled").getBoolean());
return this;
}
public Builder resolveExtraPlayersCount(){
if(node.node("playerCount", "extraPlayers", "amount").virtual()){
this.extraPlayersCount = null;
return this;
}
this.extraPlayersCount = node.node("playerCount", "extraPlayers", "amount").getInt();
return this;
}
public ProfileEntry build(){
return new ProfileEntry(this.motd, this.players, this.playerCountText,
this.favicon, this.hidePlayersEnabled, this.extraPlayersEnabled, this.extraPlayersCount);
}
private List<String> resolveList(ConfigurationNode node, Object... path){
try{
return node.node(path).getList(String.class);
}catch(SerializationException ex){
return Collections.emptyList();
}
}
}
}
@@ -0,0 +1,139 @@
/*
* 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.core.profiles.profile;
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.objects.NullBool;
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
import java.util.List;
public class ProfileManager{
private final AdvancedServerList core;
private GenericPlayer player;
private GenericServer server;
private ProfileManager(AdvancedServerList core){
this.core = core;
}
public static ProfileManager get(AdvancedServerList core){
return new ProfileManager(core);
}
public ProfileManager applyReplacements(GenericPlayer player, GenericServer server){
this.player = player;
this.server = server;
return this;
}
public ServerListProfile getProfile(){
for(ServerListProfile profile : core.getFileHandler().getProfiles()){
if(profile.isInvalidProfile())
continue;
if(profile.evalConditions(player, server))
return profile;
}
return null;
}
/*
* Convenience method to get a ProfileEntry instance with values merged.
* If the random profile's value is not null will it be used instead of the default one.
*/
public static ProfileEntry merge(ServerListProfile profile){
ProfileEntry entry = profile.getRandomProfile();
ProfileEntry defEntry = profile.getDefaultProfile();
List<String> motd = resolveMOTD(entry, defEntry);
List<String> players = resolvePlayers(entry, defEntry);
String playerCountText = resolvePlayerCountText(entry, defEntry);
String favicon = resolveFavicon(entry, defEntry);
boolean isHidePlayersEnabled = resolveHidePlayersEnabled(entry, defEntry);
boolean isExtraPlayersEnabled = resolveExtraPlayersEnabled(entry, defEntry);
Integer extraPlayersCount = resolveExtraPlayersCount(entry, defEntry);
return new ProfileEntry(motd, players, playerCountText, favicon,
new NullBool(isHidePlayersEnabled), new NullBool(isExtraPlayersEnabled), extraPlayersCount);
}
private static List<String> resolveMOTD(ProfileEntry profile, ProfileEntry defaultProfile){
if(profile == null || profile.getMOTD().isEmpty())
return defaultProfile.getMOTD();
return profile.getMOTD();
}
private static List<String> resolvePlayers(ProfileEntry profile, ProfileEntry defaultProfile){
if(profile == null || profile.getPlayers().isEmpty())
return defaultProfile.getPlayers();
return profile.getPlayers();
}
private static String resolvePlayerCountText(ProfileEntry profile, ProfileEntry defaultProfile){
if(profile == null || profile.getPlayerCountText().isEmpty())
return defaultProfile.getPlayerCountText();
return profile.getPlayerCountText();
}
private static String resolveFavicon(ProfileEntry profile, ProfileEntry defaultProfile){
if(profile == null || profile.getFavicon().isEmpty())
return defaultProfile.getFavicon();
return profile.getFavicon();
}
private static boolean resolveHidePlayersEnabled(ProfileEntry profile, ProfileEntry defaultProfile){
if(profile == null || profile.isHidePlayersEnabled().isNull())
return defaultProfile.isHidePlayersEnabled().getValue(false);
return profile.isHidePlayersEnabled().getValue(false);
}
private static boolean resolveExtraPlayersEnabled(ProfileEntry profile, ProfileEntry defaultProfile){
if(profile == null || profile.isExtraPlayersEnabled().isNull())
return defaultProfile.isExtraPlayersEnabled().getValue(false);
return profile.isExtraPlayersEnabled().getValue(false);
}
private static Integer resolveExtraPlayersCount(ProfileEntry profile, ProfileEntry defaultProfile){
if(profile == null || profile.getExtraPlayersCount() == null)
return defaultProfile.getExtraPlayersCount() == null ? 0 : defaultProfile.getExtraPlayersCount();
return profile.getExtraPlayersCount();
}
}
@@ -0,0 +1,58 @@
/*
* 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.core.profiles.profile;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.serialize.SerializationException;
import org.spongepowered.configurate.serialize.TypeSerializer;
import java.lang.reflect.Type;
public class ProfileSerializer implements TypeSerializer<ProfileEntry>{
public static final ProfileSerializer INSTANCE = new ProfileSerializer();
@Override
public ProfileEntry deserialize(Type type, ConfigurationNode node){
return ProfileEntry.Builder.resolve(node).build();
}
@Override
public void serialize(Type type, @Nullable ProfileEntry profile, ConfigurationNode node) throws SerializationException{
if(profile == null){
node.raw(null);
return;
}
node.node("motd").set(profile.getMOTD());
node.node("playerCount", "hover").set(profile.getPlayers());
node.node("playerCount", "text").set(profile.getPlayerCountText());
node.node("favicon").set(profile.getFavicon());
node.node("playerCount", "hidePlayers").set(profile.isHidePlayersEnabled());
node.node("playerCount", "extraPlayers", "enabled").set(profile.isExtraPlayersEnabled());
node.node("playerCount", "extraPlayers", "amount").set(profile.getExtraPlayersCount());
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -25,48 +25,107 @@
package ch.andre601.advancedserverlist.core.profiles.replacer;
import java.util.Iterator;
import java.util.Map;
import ch.andre601.advancedserverlist.api.AdvancedServerListAPI;
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
/*
* Original by Imo van den Berge (aka Bergerkiller: https://github.com/Bergerkiller)
*
* Original Source: https://github.com/bergerhealer/BKCommonLib/blob/master/src/main/java/com/bergerkiller/bukkit/common/StringReplaceBundle.java
*/
public class StringReplacer{
private final EntryList<String, Object> entries = new EntryList<>();
public void add(String from, Object to){
if(from == null || to == null)
return;
public static String replace(String input, GenericPlayer player, GenericServer server){
char[] chars = input.toCharArray();
StringBuilder builder = new StringBuilder(input.length());
entries.add(from, to);
}
public Object get(String from){
Iterator<Map.Entry<String, Object>> iterator = entries.iterator();
Map.Entry<String, Object> entry;
while(iterator.hasNext()){
entry = iterator.next();
if(entry.getKey().equals(from)){
iterator.remove();
return entry.getValue();
StringBuilder identifier = new StringBuilder();
StringBuilder placeholder = new StringBuilder();
AdvancedServerListAPI api = AdvancedServerListAPI.get();
for(int i = 0; i < chars.length; i++){
char c = chars[i];
char next = (i == (chars.length - 1)) ? '\0' : chars[i + 1];
if(c != '$' || i + 2 >= chars.length){
builder.append(c);
continue;
}
}
return null;
}
public String replace(String input){
StringBuilder output = new StringBuilder(input);
int index;
for(Map.Entry<String, Object> entry : entries){
index = 0;
while((index = output.indexOf(entry.getKey(), index)) != -1){
output.replace(index, index + entry.getKey().length(), String.valueOf(entry.getValue()));
index += String.valueOf(entry.getValue()).length();
if(next != '{'){
if(next == '\0')
continue;
builder.append(next);
i++;
continue;
}
i++;
boolean identified = false;
boolean invalid = true;
while(++i < chars.length){
final char id = chars[i];
if(id == '}'){
invalid = false;
break;
}
if(id == ' ' && !identified){
identified = true;
continue;
}
if(identified){
placeholder.append(id);
}else{
identifier.append(id);
}
}
String identifierString = identifier.toString();
String placeholderString = placeholder.toString();
identifier.setLength(0);
placeholder.setLength(0);
if(invalid){
builder.append("${").append(identifierString);
if(identified){
builder.append(' ').append(placeholderString);
}
continue;
}
PlaceholderProvider provider = api.retrievePlaceholderProvider(identifierString);
if(provider == null){
builder.append("${").append(identifierString);
if(identified){
builder.append(' ').append(placeholderString);
}
builder.append('}');
continue;
}
String replacement = provider.parsePlaceholder(placeholderString, player, server);
if(replacement == null){
builder.append("${").append(identifierString);
if(identified){
builder.append(' ').append(placeholderString);
}
builder.append('}');
continue;
}
builder.append(replacement);
}
return output.toString();
return builder.toString();
}
}
+31 -1
View File
@@ -1,4 +1,34 @@
#
# Used for when AdvancedServerList doesn't have a cached player to use for ${player name}
#
unknown_player: "Anonymous"
# Defaults to 'Anonymous' when not present.
#
unknown_player: "Anonymous"
#
# UUID used for when AdvancedServerList doesn't have a cached player to use for the ${player uuid}
# placeholder.
#
# Defaults to the UUID of MHF_Question (606e2ff0-ed77-4842-9d6c-e1d3321c7838) when not present.
#
unknown_player_uuid: "606e2ff0-ed77-4842-9d6c-e1d3321c7838"
#
# Should the Caching of players be disabled?
#
# When set to true will AdvancedServerList not load any players from the cache.data file (if present) nor save any to it.
# Placeholders that rely on the player being cached (such as ${player isWhitelisted}) will not work while this option is enabled.
#
# Defaults to false when not present.
#
disable_cache: false
#
# Should AdvancedServerList check for new versions?
#
# When set to true will AdvancedServerList check for a new version on modrinth.com every 12 hours and inform you in the
# console about whether it found any new update or not.
#
# Defaults to false when not present.
#
check_updates: true
+109 -15
View File
@@ -16,30 +16,124 @@ priority: 0
# - '{playerVersion} < 759'
#
# Set the MOTD that should be displayed.
# Can be removed or set to "motd: []" to not alter the MOTD.
# Option that allows you to define multiple profiles to use.
# Each entry supports the same options as the server list profile does. Whenever an option is not present, or set to
# an empty value (If supported) would the option in the file be used (if present).
# This allows you to f.e. randomize the MOTD while keeping other options the same, without copy-pasting the same options.
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#motd
# A profile will be selected randomly.
#
# Remove this option or set it to profiles: [] to use the global options of this file.
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#profiles
#
#profiles:
# - motd:
# - '<aqua>Line 1'
# - '<aqua>Line 2'
# favicon: ''
# playerCount:
# hidePlayers: false
# hover:
# - '<green>Line 1'
# - '<green>Line 2'
# - '<green>Line 3'
# text: ''
# extraPlayers:
# enabled: false
# amount: 0
#
# Set the default (Global) MOTD used by this server list profile.
# Supports HEX colors for 1.16+ servers including gradients using MiniMessage's <gradient:color:color> format.
#
# Remove this option or set it to motd: [] to not alter the MOTD.
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#motds
#
motd:
- 'Line 1'
- 'Line 2'
- '<grey>Line 1'
- '<grey>Line 2'
#
# Set the hover text that usually displays online players.
# Can be removed or set to "players: []" to not alter the player list.
# Allows you to set a global (Default) favicon for this server list profile.
# You can use one of three possible options:
# 1. A file name matching a PNG file located in the 'favicons' folder (Needs to end with .png)
# 2. A URL pointing to a valid Image
# 3. ${player name} to display the player's head as favicon. Uses https://mc-heads.net for this
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#players
# Remove this option or set to favicon: '' to not replace the Favicon.
#
players:
- 'Line 1'
- 'Line 2'
- 'Line 3'
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#favicon
#
favicon: ''
#
# Set the text that usually shows online players and max that can join.
# Can be removed or set to "playerCount: ''" to not alter the player count text.
# Contains various options to manipulate the player count (Text displaying online and max players allowed).
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#playercount
#
playerCount: 'Text'
playerCount:
#
# Whether AdvancedServerList should hide the player count or not.
# When set to true will the player count display '???' instead of <online>/<max>
#
# Note that when this is enabled will all other options, except for 'extraPlayers', be ignored.
#
# Defaults to false when not present.
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#hideplayers
#
hidePlayers: false
#
# Sets the text displayed when hovering over the player count.
#
# Remove this option or set it to hover: [] to not modify the player samples on the player count.
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#hover
#
hover:
- '<grey>Line 1'
- '<grey>Line 2'
- '<grey>Line 3'
#
# Modifies the text that usually displays '<online>/<max>'
#
# Note that when used, the Ping icon will display as 'outdated server'/'outdated client'.
# This is nothing that can be fixed or avoided and would require changes from Mojang.
#
# Remove this option or set it to text: '' to not modify the player count text.
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#text
#
text: ''
#
# Allows you to modify the max player count displayed.
# Note that this will affect ${server playersMax} (Except for when used in conditions).
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#extraPlayers
#
extraPlayers:
#
# Enables/Disables the extraPlayers option.
# When enabled will the total amount of players allowed be modified to the current
# number of online players + 'amount'
#
# Defaults to false when not present.
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#enabled
#
enabled: false
#
# Sets how much should be added/subtracted from the current number of online players
# to then use for the max player count.
#
# Example:
# When set to 1 while 10 players are online would the player count - if unmodified -
# display '10/11'. When -1 is used will it display '10/9'
#
# Defaults to 0 when not present.
#
# Read more: https://github.com/Andre601/AdvancedServerList/wiki/Profiles#amount
#
amount: 0
@@ -0,0 +1,26 @@
#
# 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.
#
#
version=${plugin.version}
@@ -0,0 +1,292 @@
/*
* 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.test;
import com.unascribed.flexver.FlexVerComparator;
import org.junit.Test;
public class FlexVerTests{
private final String baseVersion = "1.0.0";
private final String versionMinor = "1.0.1";
private final String versionMajor = "1.1.0";
private final String versionBoth = "1.1.1";
private final String versionBeta = "1.0.0-b1";
private final String versionBetaMinor = "1.0.1-b1";
private final String versionBetaMajor = "1.1.0-b1";
private final String versionBetaBoth = "1.1.1-b1";
private final String versionBeta2 = "1.0.0-b2";
private final String versionBeta2Minor = "1.0.1-b2";
private final String versionBeta2Major = "1.1.0-b2";
private final String versionBeta2Both = "1.1.1-b2";
@Test
public void testFlexVerChecks(){
System.out.println("START: Version comparison Tests");
compareBaseVersion();
compareMinorVersion();
compareMajorVersion();
compareBothVersion();
compareBetaVersion();
compareBetaMinorVersion();
compareBetaMajorVersion();
compareBetaBothVersion();
compareBeta2Version();
compareBeta2MinorVersion();
compareBeta2MajorVersion();
compareBeta2BothVersion();
System.out.println("END: Version comparison Tests");
}
private void compareBaseVersion(){
System.out.println("┌────────────────────────────────┐");
System.out.println("│ Normal Version │");
System.out.println("│ │");
compare(baseVersion, "=", baseVersion, 0);
compare(baseVersion, "<", versionMinor, -1);
compare(baseVersion, "<", versionMajor, -1);
compare(baseVersion, "<", versionBoth, -1);
compare(baseVersion, ">", versionBeta, 1);
compare(baseVersion, "<", versionBetaMinor, -1);
compare(baseVersion, "<", versionBetaMajor, -1);
compare(baseVersion, "<", versionBetaBoth, -1);
compare(baseVersion, ">", versionBeta2, 1);
compare(baseVersion, "<", versionBeta2Minor, -1);
compare(baseVersion, "<", versionBeta2Major, -1);
compare(baseVersion, "<", versionBeta2Both, -1);
System.out.println("├────────────────────────────────┤");
}
private void compareMinorVersion(){
System.out.println("│ Minor Version │");
System.out.println("│ │");
compare(versionMinor, ">", baseVersion, 1);
compare(versionMinor, "=", versionMinor, 0);
compare(versionMinor, "<", versionMajor, -1);
compare(versionMinor, "<", versionBoth, -1);
compare(versionMinor, ">", versionBeta, 1);
compare(versionMinor, ">", versionBetaMinor, 1);
compare(versionMinor, "<", versionBetaMajor, -1);
compare(versionMinor, "<", versionBetaBoth, -1);
compare(versionMinor, ">", versionBeta2, 1);
compare(versionMinor, ">", versionBeta2Minor, 1);
compare(versionMinor, "<", versionBeta2Major, -1);
compare(versionMinor, "<", versionBeta2Both, -1);
System.out.println("├────────────────────────────────┤");
}
private void compareMajorVersion(){
System.out.println("│ Major Version │");
System.out.println("│ │");
compare(versionMajor, ">", baseVersion, 1);
compare(versionMajor, ">", versionMinor, 1);
compare(versionMajor, "=", versionMajor, 0);
compare(versionMajor, "<", versionBoth, -1);
compare(versionMajor, ">", versionBeta, 1);
compare(versionMajor, ">", versionBetaMinor, 1);
compare(versionMajor, ">", versionBetaMajor, 1);
compare(versionMajor, "<", versionBetaBoth, -1);
compare(versionMajor, ">", versionBeta2, 1);
compare(versionMajor, ">", versionBeta2Minor, 1);
compare(versionMajor, ">", versionBeta2Major, 1);
compare(versionMajor, "<", versionBeta2Both, -1);
System.out.println("├────────────────────────────────┤");
}
private void compareBothVersion(){
System.out.println("│ Major + Minor Version │");
System.out.println("│ │");
compare(versionBoth, ">", baseVersion, 1);
compare(versionBoth, ">", versionMinor, 1);
compare(versionBoth, ">", versionMajor, 1);
compare(versionBoth, "=", versionBoth, 0);
compare(versionBoth, ">", versionBeta, 1);
compare(versionBoth, ">", versionBetaMinor, 1);
compare(versionBoth, ">", versionBetaMajor, 1);
compare(versionBoth, ">", versionBetaBoth, 1);
compare(versionBoth, ">", versionBeta2, 1);
compare(versionBoth, ">", versionBeta2Minor, 1);
compare(versionBoth, ">", versionBeta2Major, 1);
compare(versionBoth, ">", versionBeta2Both, 1);
System.out.println("├────────────────────────────────┤");
}
private void compareBetaVersion(){
System.out.println("│ Beta Version │");
System.out.println("│ │");
compare(versionBeta, "<", baseVersion, -1);
compare(versionBeta, "<", versionMinor, -1);
compare(versionBeta, "<", versionMajor, -1);
compare(versionBeta, "<", versionBoth, -1);
compare(versionBeta, "=", versionBeta, 0);
compare(versionBeta, "<", versionBetaMinor, -1);
compare(versionBeta, "<", versionBetaMajor, -1);
compare(versionBeta, "<", versionBetaBoth, -1);
compare(versionBeta, "<", versionBeta2, -1);
compare(versionBeta, "<", versionBeta2Minor, -1);
compare(versionBeta, "<", versionBeta2Major, -1);
compare(versionBeta, "<", versionBeta2Both, -1);
System.out.println("├────────────────────────────────┤");
}
private void compareBetaMinorVersion(){
System.out.println("│ Beta Version Minor │");
System.out.println("│ │");
compare(versionBetaMinor, ">", baseVersion, 1);
compare(versionBetaMinor, "<", versionMinor, -1);
compare(versionBetaMinor, "<", versionMajor, -1);
compare(versionBetaMinor, "<", versionBoth, -1);
compare(versionBetaMinor, ">", versionBeta, 1);
compare(versionBetaMinor, "=", versionBetaMinor, 0);
compare(versionBetaMinor, "<", versionBetaMajor, -1);
compare(versionBetaMinor, "<", versionBetaBoth, -1);
compare(versionBetaMinor, ">", versionBeta2, 1);
compare(versionBetaMinor, "<", versionBeta2Minor, -1);
compare(versionBetaMinor, "<", versionBeta2Major, -1);
compare(versionBetaMinor, "<", versionBeta2Both, -1);
System.out.println("├────────────────────────────────┤");
}
private void compareBetaMajorVersion(){
System.out.println("│ Beta Version Major │");
System.out.println("│ │");
compare(versionBetaMajor, ">", baseVersion, 1);
compare(versionBetaMajor, ">", versionMinor, 1);
compare(versionBetaMajor, "<", versionMajor, -1);
compare(versionBetaMajor, "<", versionBoth, -1);
compare(versionBetaMajor, ">", versionBeta, 1);
compare(versionBetaMajor, ">", versionBetaMinor, 1);
compare(versionBetaMajor, "=", versionBetaMajor, 0);
compare(versionBetaMajor, "<", versionBetaBoth, -1);
compare(versionBetaMajor, ">", versionBeta2, 1);
compare(versionBetaMajor, ">", versionBeta2Minor, 1);
compare(versionBetaMajor, "<", versionBeta2Major, -1);
compare(versionBetaMajor, "<", versionBeta2Both, -1);
System.out.println("├────────────────────────────────┤");
}
private void compareBetaBothVersion(){
System.out.println("│ Beta Version Major + Minor │");
System.out.println("│ │");
compare(versionBetaBoth, ">", baseVersion, 1);
compare(versionBetaBoth, ">", versionMinor, 1);
compare(versionBetaBoth, ">", versionMajor, 1);
compare(versionBetaBoth, "<", versionBoth, -1);
compare(versionBetaBoth, ">", versionBeta, 1);
compare(versionBetaBoth, ">", versionBetaMinor, 1);
compare(versionBetaBoth, ">", versionBetaMajor, 1);
compare(versionBetaBoth, "=", versionBetaBoth, 0);
compare(versionBetaBoth, ">", versionBeta2, 1);
compare(versionBetaBoth, ">", versionBeta2Minor, 1);
compare(versionBetaBoth, ">", versionBeta2Major, 1);
compare(versionBetaBoth, "<", versionBeta2Both, -1);
System.out.println("├────────────────────────────────┤");
}
private void compareBeta2Version(){
System.out.println("│ Beta Version 2 │");
System.out.println("│ │");
compare(versionBeta2, "<", baseVersion, -1);
compare(versionBeta2, "<", versionMinor, -1);
compare(versionBeta2, "<", versionMajor, -1);
compare(versionBeta2, "<", versionBoth, -1);
compare(versionBeta2, ">", versionBeta, 1);
compare(versionBeta2, "<", versionBetaMinor, -1);
compare(versionBeta2, "<", versionBetaMajor, -1);
compare(versionBeta2, "<", versionBetaBoth, -1);
compare(versionBeta2, "=", versionBeta2, 0);
compare(versionBeta2, "<", versionBeta2Minor, -1);
compare(versionBeta2, "<", versionBeta2Major, -1);
compare(versionBeta2, "<", versionBeta2Both, -1);
System.out.println("├────────────────────────────────┤");
}
private void compareBeta2MinorVersion(){
System.out.println("│ Beta Version 2 Minor │");
System.out.println("│ │");
compare(versionBeta2Minor, ">", baseVersion, 1);
compare(versionBeta2Minor, "<", versionMinor, -1);
compare(versionBeta2Minor, "<", versionMajor, -1);
compare(versionBeta2Minor, "<", versionBoth, -1);
compare(versionBeta2Minor, ">", versionBeta, 1);
compare(versionBeta2Minor, ">", versionBetaMinor, 1);
compare(versionBeta2Minor, "<", versionBetaMajor, -1);
compare(versionBeta2Minor, "<", versionBetaBoth, -1);
compare(versionBeta2Minor, ">", versionBeta2, 1);
compare(versionBeta2Minor, "=", versionBeta2Minor, 0);
compare(versionBeta2Minor, "<", versionBeta2Major, -1);
compare(versionBeta2Minor, "<", versionBeta2Both, -1);
System.out.println("├────────────────────────────────┤");
}
private void compareBeta2MajorVersion(){
System.out.println("│ Beta Version 2 Major │");
System.out.println("│ │");
compare(versionBeta2Major, ">", baseVersion, 1);
compare(versionBeta2Major, ">", versionMinor, 1);
compare(versionBeta2Major, "<", versionMajor, -1);
compare(versionBeta2Major, "<", versionBoth, -1);
compare(versionBeta2Major, ">", versionBeta, 1);
compare(versionBeta2Major, ">", versionBetaMinor, 1);
compare(versionBeta2Major, ">", versionBetaMajor, 1);
compare(versionBeta2Major, "<", versionBetaBoth, -1);
compare(versionBeta2Major, ">", versionBeta2, 1);
compare(versionBeta2Major, ">", versionBeta2Minor, 1);
compare(versionBeta2Major, "=", versionBeta2Major, 0);
compare(versionBeta2Major, "<", versionBeta2Both, -1);
System.out.println("├────────────────────────────────┤");
}
private void compareBeta2BothVersion(){
System.out.println("│ Beta Version 2 Major + Minor │");
System.out.println("│ │");
compare(versionBeta2Both, ">", baseVersion, 1);
compare(versionBeta2Both, ">", versionMinor, 1);
compare(versionBeta2Both, ">", versionMajor, 1);
compare(versionBeta2Both, "<", versionBoth, -1);
compare(versionBeta2Both, ">", versionBeta, 1);
compare(versionBeta2Both, ">", versionBetaMinor, 1);
compare(versionBeta2Both, ">", versionBetaMajor, 1);
compare(versionBeta2Both, ">", versionBetaBoth, 1);
compare(versionBeta2Both, ">", versionBeta2, 1);
compare(versionBeta2Both, ">", versionBeta2Minor, 1);
compare(versionBeta2Both, ">", versionBeta2Major, 1);
compare(versionBeta2Both, "=", versionBeta2Both, 0);
System.out.println("└────────────────────────────────┘");
}
private void compare(String version1, String comparator, String version2, int expected){
String expression = version1 + " " + comparator + " " + version2 + "?";
System.out.printf("│ %-24s %5b │\n", expression, FlexVerComparator.compare(version1, version2) == expected);
}
}
+142
View File
@@ -0,0 +1,142 @@
# API
AdvancedServerList v2 introduced a new API that plugins can hook into to use.
As of right now is it only providing a way to add your own placeholders for it to use.
## Add dependency
Add the following to your `build.gradle` or `pom.xml` file to use the API:
=== ":simple-gradle: Gradle"
Make sure to replace `{version}` with the latest version available in the [GitHub Repository][api-repo].
```groovy
repositorories {
maven { url = 'https://jitpack.io/" }
}
dependencies {
implementation 'ch.andre601.asl-api:api:{version}'
// Optional platform dependencies
implementation 'ch.andre601.asl-api:platform-bungeecord:{version}'
implementation 'ch.andre601.asl-api:platform-paper:{version}'
implementation 'ch.andre601.asl-api:platform-spigot:{version}'
implementation 'ch.andre601.asl-api:platform-velocity:{version}'
}
```
=== ":simple-apachemaven: Maven"
Make sure to replace `{version}` with the latest version available in the [GitHub Repository][api-repo].
```xml
<repositories>
<repository>
<id>jitpack</id>
<url>https://jitpack.io/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>ch.andre601.asl-api</groupId>
<artifactId>api</artifactId>
<version>{version}</version>
</dependency>
<!-- Optional platform dependencies -->
<dependency>
<groupId>ch.andre601.asl-api</groupId>
<artifactId>platform-bungeecord</artifactId>
<version>{version}</version>
</dependency>
<dependency>
<groupId>ch.andre601.asl-api</groupId>
<artifactId>platform-paper</artifactId>
<version>{version}</version>
</dependency>
<dependency>
<groupId>ch.andre601.asl-api</groupId>
<artifactId>platform-spigot</artifactId>
<version>{version}</version>
</dependency>
<dependency>
<groupId>ch.andre601.asl-api</groupId>
<artifactId>platform-velocity</artifactId>
<version>{version}</version>
</dependency>
</dependencies>
```
[api-repo]: https://github.com/Andre601/asl-api
## Add own placeholders
To add your own placeholders will you need to do a few steps.
### 1) Get API instance
Use `AdvancedServerListAPI.get()` to retrieve an instance of the currently used AdvancedServerList API.
It will be needed at a later point.
### 2) Create a Placeholder class
Make a new class that you want to use for the placeholders and let it extend the `PlaceholderProvider` class of AdvancedServerList.
Your IDE should now tell you to implement/override some methods. Confirm this action and your class should look something alongside this:
```java
public class MyPlaceholders extends PlaceholderProvider {
public MyPlaceholders(String identifier) {
super(identifier);
}
@Override
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server) {
return null;
}
}
```
It's recommended to replace your generated constructor with a no-args one and set the identifier directly in the `super()`.
For example:
```java
public MyPlaceholders() {
super("myplaceholders");
}
```
The next step now would be to handle the different placeholders. These are handled in the `parsePlaceholder` method you had to override.
The `placeholder` String is whatever value was provided after the identifier in `${<identifier> <placeholder>}`. It can contain spaces.
Something to note is, that when returning `null` will AdvancedServerList understand it as an invalid placeholder and return it unchanged.
Here is a small example of the final class:
```java
public class MyPlaceholders extends PlaceholderProvider {
public MyPlaceholders() {
super("myplaceholders");
}
@Override
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server) {
if(placeholder.equals("hello"))
return "Hi!";
return null;
}
}
```
### 3) Register the placeholder class
All that is left to do now is to register your class as a new PlaceholderProvider instance. To do this, get the API instance you retrieved earlier and use `addPlaceholderProvider` with a new instance of your class.
Example:
```java
AdvancedServerListAPI api = AdvancedServerListAPI.get();
api.addPlaceholderProvider(new MyPlaceholders());
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

+8
View File
@@ -0,0 +1,8 @@
.md-header__button.md-logo img {
height: 2.5rem;
}
.md-header__button.md-logo {
padding-right: unset;
padding-left: unset;
}
+149
View File
@@ -0,0 +1,149 @@
## About
!!! warning "The below examples may only work in v1.10.0 or newer!"
This page lists some examples you may want to use for your proxy or server.
Feel free to suggest additional examples on my [Discord Server](https://discord.gg/6dazXp6) in the `#advancedserverlist` channel.
## Table of Contents
- [Versions](#versions)
- [Different List for outdated Clients](#different-list-for-outdated-clients)
- [Different MOTD for < 1.16](#different-motd-for-116)
- [Spigot/PaperMC](#spigotpapermc)
- [Different profile for banned Player](#different-profile-for-banned-player)
- [Other](#other)
- [Different profile for different host](#different-profile-for-different-host)
- [Different profile for known player](#different-profile-for-known-player)
- [Random MOTD but same hover](#random-motd-but-same-hover)
----
## Versions
Some examples about version-related things.
### Different List for outdated Clients
You may want to display a separate MOTD or text for incompatible client versions.
This example here shows a separate MOTD and player count message for when the player's client version is older than what the server supports.
```yaml
priority: 1
# Display this profile, if the version of the player is below 760 (1.19.1)
conditions:
- '${player protocol} < 760'
motd:
- <red>You are using an outdated client!
- <red>Please update to 1.19.1 to join.
playerCount:
text: '<red>Outdated Client!'
```
### Different MOTD for < 1.16
This profile would display a MOTD without HEX colours if the player's version is below 735 (1.16).
```yaml
priority: 1
# Display this profile, if the version of the player is below 735 (1.16)
conditions:
- '${player protocol} < 735'
motd:
- <grey>Welcome to <aqua>YourServer.com</aqua>!
- <grey>Enjoy your stay.
```
----
## Spigot/PaperMC
Examples that can be used with the Spigot or PaperMC version.
### Different profile for banned Player
!!! warning "Requires a cached player to work reliably."
Displays a separate profile for when the player has been banned from the server.
```yaml
priority: 1
conditions:
- '${player isBanned} = true'
motd:
- <red>You have been <bold>BANNED</bold>!
- <red>Go away.
# Don't show the player count to the banned player.
playerCount:
hidePlayers: true
```
----
## Other
Some misc examples.
### Different profile for different host
Display a different profile when the playing pings a specific domain/IP.
```yaml
priority: 1
conditions:
- '${server host} = other.example.com'
motd:
- <grey>Please use <aqua>mc.example.com
- <grey>to join our server.
```
### Different profile for known player
Display a different profile, if the player pinging the server is "known" by AdvancedServerList (Has joined before with the same IP).
```yaml
priority: 1
conditions:
- `${player name} != Anonymous` # "Anonymous" should be replaced with whatever you use in 'unknown_player'
motd:
- <grey>Hello there <aqua>${player name}</aqua>!
- <grey>It's nice to see you again.
playerCount:
hover:
- '<grey>Welcome back <aqua>${player name}</aqua>!'
- '<grey>'
- '<grey>Come and join <aqua>${server playersOnline}'
- '<aqua>other players</aqua> <grey>on the Server!'
```
### Random MOTD but same hover
v1.10.0 of AdvancedServerList introduced an option called `profiles`. It allows you to create multiple variants of an MOTD, playercount, etc. while also supporting the normal (global) options as a fallback.
The following example will create a profile that has a randomized MOTD, but the same hover for both:
```yaml
priority: 1
profiles:
- motd:
- 'A randomized MOTD.'
- 'Lines change, but hover is the same.'
- motd: ['Different Syntax.', 'May be more readable.']
playerCount:
hover:
- 'This player count hover always'
- 'stays the same, no matter what'
- 'MOTD is being used here.'
```
+34
View File
@@ -0,0 +1,34 @@
# AdvancedServerList Wiki
Welcome to the Wiki of AdvancedServerList!
On this site can you find the latest documentation for the plugin, including some useful examples.
If you're new to the plugin is it recommended to check out the [Profiles page](./profiles) for a quick start on how to use this plugin.
## Platforms
AdvancedServerList supports the following platforms:
<a href="https://www.spigotmc.org" target="_blank">
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@2/assets/minimal/supported/spigot_vector.svg" width="64" alt="platform-spigot" title="Tested on Spigot">
</a>
<a href="https://papermc.io" target="_blank">
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@2/assets/minimal/supported/paper_vector.svg" width="64" alt="platform-paper" title="Tested on Paper">
</a>
<a href="https://www.spigotmc.org" target="_blank">
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@2/assets/minimal/supported/bungeecord_vector.svg" width="64" alt="platform-bungeecord" title="Tested on BungeeCord">
</a>
<a href="https://www.papermc.io" target="_blank">
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@2/assets/minimal/supported/waterfall_vector.svg" width="64" alt="platform-waterfall" title="Tested on Waterfall">
</a>
<a href="https://velocitypowered.com" target="_blank">
<img src="https://cdn.jsdelivr.net/npm/@intergrav/devins-badges@2/assets/minimal/supported/velocity_vector.svg" width="64" alt="platform-velocity" title="Tested on Velocity">
</a>
## Pages
- [Profiles](./profiles)
- [Formatting](./profiles/formatting)
- [Placeholders](./profiles/placeholders)
- [API](./api)
- [Examples](./examples)
+21
View File
@@ -0,0 +1,21 @@
# Formatting
All text options with exception of [`Conditions`](../#conditions) allow the usage of formatting options using [MiniMessage].
## Unsupported options
The following options are **not** supported, no matter what option they are used in:
- Hover Actions (Show text, Show Advancement, etc)
- Click Actions (Run command, Suggest command, etc.)
- Custom Fonts (May work if player already has the resource pack loaded)
## Colors
Normal text colours such as `<aqua>` or `<red>` can be used in all text options.
## HEX Colours
24-Bit HEX colours such as `<#ffffff>` can be used in the [`Motd`](../#motd) option on 1.16+ servers/proxies.
[MiniMessage]: https://docs.adventure.kyori.net/minimessage/index.html
+157
View File
@@ -0,0 +1,157 @@
# Profiles
AdvancedServerList allows the creation of multiple profiles.
A profile is a single YAML file located in the plugin's `profiles` directory containing multiple settings including [Conditions](#conditions) and [Priority](#priority).
!!! info "Important"
In order for a profile to be considered "valid" by AdvancedServerList will it need to meet the following conditions:
- Have a [`Priority`](#priority) set to a valid, whole number (i.e. `1`)
- Have **at least one** of the following options set:
- [`Profiles`](#profiles) with at least one entry containing one of the below option
- [`Motd`](#motd) set with at least one line of text
- [`Favicon`](#favicon) set with a non-empty String
- [`HidePlayers`](#hideplayers) set to `true`
- [`Hover`](#hover) set with at least one line of text
- [`Text`](#text) set with a non-empty String
## Priority
The priority option determines the order in which the profiles are checked by AdvancedServerList, starting with the highest number.
It will go through each file until it finds one with [`conditions`](#conditions) returning true (Default if no conditions are set).
This means that a profile with no conditions and a priority of `1` will be used before a profile with a condition and priority `0` even if the player is meeting the condition.
## Conditions
A list of conditions can be set that need to be met in order to have this profile shown to the player.
[Placeholders](./formatting#placeholders) can be used to further customize the conditions (PlaceholderAPI placeholders are **not** supported).
Only when **all** conditions defined return true will the profile be displayed.
You can remove this option, or set it to an empty list (`conditions: []`) to always return true.
### Operants
The following operants are available and can be used in the conditions.
!!! warning "Only one operant can be used per condition!"
| Operant | Description |
| ------- | ------------------------------------------------------------------ |
| `<` | Checks if the left value is less than the right one.\* |
| `<=` | Checks if the left value is less than or equal to the right one.\* |
| `>` | Checks if the left value is more than the right one.\* |
| `>=` | Checks if the left value is more than or equal to the right one.\* |
| `=` | Checks if the left value is equal to the right one. |
| `!=` | Checks if the left value is not equal to the right one. |
\* In the case of the provided value not being a number will the plugin instead use the text length to compare with.
## Profiles
!!! info "Available since v1.10.0"
The profiles option allows you to add multiple combinations of all the other options (With the exception of [`Priority`](#priority) and [`Conditions`](#conditions)) to have randomized MOTDs, player counts, etc.
If an option is not present in an entry will AdvancedServerList try to use one defined in the file or use whatever default value would be for that option.
As an example:
```yaml
priority: 1
profiles:
- motd:
- Line 1
- Line 2
playerCount:
text: 'Awesome!'
- playerCount:
text: 'Also Awesome'
motd:
- Line A
- Line B
```
...the above file would randomly select between a MOTD saying `Line 1\nLine 2` and a player count saying `Awesome!` or a MOTD saying `Line A\nLine B` while the player count says `Also Awesome`
## Motds
!!! warning "Only available in v1.9.0"
This option allows to create multiple MOTDs that AdvancedServerList should randomly select from.
Due to the structure of this option will you need to use `|-` to have multi-line MOTDs.
Example:
```yaml
motds:
- |-
Line 1
Line 2
- |-
Line A
Line B
```
## Motd
The motd option allows to set up to two lines to be displayed in an MOTD.
Normal colour and formatting codes such as `<aqua>` or `<bold>` are supported. On 1.16 and newer can you also use HEX colours using either `<#rrggbb>` or `<color:#rrggbb>`.
If you want to display multiple MOTDs will you need to use the [`Profiles` option](#profiles)
## Favicon
The favicon option allows you to customize the favicon and display one or multiple different images.
This option currently supports the following values:
- A URL pointing to a `64x64` PNG image.
- A name with `.png` extension that matches a PNG file in the `favicons` folder.
- `${player uuid}` (or alternatively `${player name}`) to display the head of the player. The UUID/Name resolving is handled by https://mc-heads.net
Please note that AdvancedServerList will rescale the image to be 64x64 pixels, so be sure to provide images at that particular scale.
## PlayerCount
The `playerCount` option contains multiple different options all about the Player count itself.
### HidePlayers
Boolean option to set whether AdvancedServerList should hide the player count or not. When set to `true` will the player count be replaced with `???`.
Additionally will the [`Text`](#text) and [`Hover`](#hover) option be ignored.
### Hover
This option allows to override the hover text usually displaying the players on the server when hovering over the player count.
You can either remove this option or set to an empty list (`hover: []`) to not override the hover.
### Text
This option allows to override the text usually displaying the online players and total players that can join.
Note that AdvancedServerList will not add the `<online>/<max>` text to the player count. Instead will you need to use [`${server playersOnline}`](./placeholders#server) and [`${server playersMax}`](./placeholders#server) respectively.
!!! info "Note"
This feature works by changing the "outdated server" message followed by altering the protocol version of the server.
Due to this will your server appear as "outdated" to the client (Have the ping icon crossed out). This is nothing that can be changed unfortunately.
To not override the player count, remove this option or set it to an empty String (`text: ''`).
### ExtraPlayers
Contains options to modify how the `<max>` number in the player count should look like.
!!! warning "Using this option will modify the `${server playersMax}` placeholder (Except in conditions)"
#### Enabled
Boolean option to set whether the extra players feature should be used or not.
When set to `true` will the max amount of players be modified by taking the current amount of online players, add [`Amount`](#amount) more to it and use it.
#### Amount
Sets the number to add to the current online players to then use as the new max players count.
As an example, setting this to `1` while `10` players are online will display `11`, while `-1` would display `9`.
+52
View File
@@ -0,0 +1,52 @@
# Placeholders
AdvancedServerList provides a set of pre-made placeholders using the `${<identifier> <placeholder>}` format adobted from BungeeTabListPlus.
It also has built-in [PlaceholderAPI support](#placeholderapi) for Spigot and Paper Servers and since v2 can even allow you to [add your own placeholders](#custom-placeholders) by using its [API](../../api).
## Built-in Placeholders
The following placeholders are available in AdvancedServerList itself. Please note that not all placeholders are available on all platforms.
### Player Placeholders
These placeholders use the player who pinged the server, to return values. They may require the player to be cached in order to work.
| Placeholder | Description | Platforms | Cached Player required?\* |
| --------------------------- | --------------------------------------------------------------- | ------------ | ------------------------ |
| `${player name}` | The name of the player. | All | Yes\*\* |
| `${player protocol}` | The protocol version of the player. | All | No |
| `${player uuid}` | The UUID of the player. | All | Yes\*\*\* |
| `${player version}` | The protocol version of the player as readable MC version. | Velocity | No |
| `${player hasPlayedBefore}` | Boolean for whether the player has played on the server before. | Spigot/Paper | Yes |
| `${player isBanned}` | Boolean for whether the player has been banned from the server. | Spigot/Paper | Yes |
| `${player isWhitelisted}` | Boolean for whether the player is whitelisted on the server. | Spigot/Paper | Yes |
\*Whether this placeholder requires the player to have joined before while AdvancedServerList was running. When `disable_cache` is enabled will these placeholders not work.
\*\*Will default to whatever name has been provided in the config.yml's `unknown_player` option, when the player isn't cached yet.
\*\*\*Will default to whatever name has been provided in the config.yml's `unknown_player_uuid` option, when the player isn't cached yet.
### Server
These placeholders use values given by the server/proxy AdvancedServerList runs on.
| Placeholder | Description |
| ------------------------- | -------------------------------------------------------- |
| `${server playersOnline}` | The number of players online on this server. |
| `${server playersMax}` | The total number of players that can join this server.\* |
| `${server host}` | The domain/IP the player pinged. |
\*This placeholder is affected by the [`Amount` option](../#amount) in a server list profile, with the exception being when used in [`Conditions`](../#conditions).
## PlaceholderAPI
The Spigot and Paper version of AdvancedServerList allow the usage of *any* placeholders provided by PlaceholderAPI by using the `%<identifier>_<values>%` placeholder format.
Depending on the placeholder used is a cached player required (i.e. `%player_name%`). AdvancedServerList will try to get a OfflinePlayer instance based on the cached UUID from the server to manage this.
!!! warning "PlaceholderAPI placeholders can NOT be used in Conditions!"
## Custom Placeholders
Since version 2 of AdvancedServerList are developers able to provide their own placeholders to be used in a server list profile.
The format follows the same one as the [built-in placeholders](#built-in-placeholders), meaning it is `${<identifier> <placeholder>}`.
If you're a developer and want to integrate your own placeholders into AdvancedServerList, read about it [here](../../api).
+492
View File
@@ -0,0 +1,492 @@
# +--------------------------------------------------------------------------------------------------------+
# | |
# | This is the main file used by MkDocs to build the pages. |
# | It contains a lot of information and settings for you to read and use. |
# | Comments may contain "Read More" URLs to more in-depth documentation about the option for you to read. |
# | |
# | You can check out https://www.mkdocs.org/user-guide/configuration/ for a more detailed explanation of |
# | all the options MkDocs offers by default. |
# | |
# +------------------------------------------------- NOTE -------------------------------------------------+
# | |
# | Some of the options listed here are only available through the usage of Material for MkDocs. |
# | Those options will usually have a link to the docs of this Theme and also mention "Material" as name. |
# | The actual name of the theme is "Material for MkDocs" and "Material" is used for simplicity reasons. |
# | |
# +--------------------------------------------------------------------------------------------------------+
# +--------------------------------------------------------------------------------------------------------+
# | |
# | Main Page Settings for MkDocs. |
# | Those settings are site name, site description, Site author and also Site URL (Canonical URL) |
# | |
# | Read More: |
# | - https://www.mkdocs.org/user-guide/configuration/#site_name |
# | - https://www.mkdocs.org/user-guide/configuration/#site_description |
# | - https://www.mkdocs.org/user-guide/configuration/#site_author |
# | - https://www.mkdocs.org/user-guide/configuration/#site_url |
# | |
# +--------------------------------------------------------------------------------------------------------+
site_name: AdvancedServerList Wiki
site_description: 'Wiki for the AdvancedServerList plugin'
site_author: 'Andre_601'
site_url: 'https://www.andre601.ch/AdvancedServerList' # Uncomment to set a canonical URL
# +--------------------------------------------------------------------------------------------------------+
# | |
# | This setting allows you to define your own Copyright notice. |
# | The text is treated as HTML code so you can use things like <a> tags or &copy; to display the |
# | Copyright icon. |
# | |
# | Where or IF the Copyright is displayed depends on the theme you use. |
# | |
# | Read More: |
# | - https://www.mkdocs.org/user-guide/configuration/#copyright |
# | |
# +--------------------------------------------------------------------------------------------------------+
copyright: |
&copy; Andre_601
# +--------------------------------------------------------------------------------------------------------+
# | |
# | The base folder to use. |
# | Any Markdown files you put into this folder will be turned into a static HTML page once you build or |
# | publish your page. |
# | |
# | It is also used as the base directory for other settings like the "extra_css" or "extra_javascript" |
# | option. |
# | |
# +--------------------------------------------------------------------------------------------------------+
docs_dir: docs/
# +--------------------------------------------------------------------------------------------------------+
# | |
# | These options allow to define a Repository to link to. |
# | The result will, depending on the theme, be a link somewhere shown on the page that links to the |
# | Repository with the specified repo_name. |
# | |
# | This will also enable a "edit" button on the page itself that allows the direct editing of the page. |
# | You can disable this by setting "edit_uri" to an empty String. |
# | |
# | Read More: |
# | - https://www.mkdocs.org/user-guide/configuration/#repo_name |
# | - https://www.mkdocs.org/user-guide/configuration/#repo_url |
# | - https://www.mkdocs.org/user-guide/configuration/#edit_uri |
# | |
# +--------------------------------------------------------------------------------------------------------+
repo_name: Andre601/AdvancedServerList
repo_url: https://github.com/Andre601/AdvancedServerList
#edit_uri: tree/master/docs # Uncomment to define a different URI/URL for the "edit" option
# +--------------------------------------------------------------------------------------------------------+
# | |
# | The "nav" option is where you define the navigation to show in MkDocs. |
# | |
# | Depending on the theme you use will the resulting Navigation look different. |
# | |
# | You can set different types of navigations. Either just the path, the path with a separate title or |
# | an external URL. |
# | |
# | Read More: |
# | - https://www.mkdocs.org/user-guide/configuration/#documentation-layout |
# | |
# +--------------------------------------------------------------------------------------------------------+
nav:
- Home: index.md
- Profiles:
- profiles/index.md
- Formatting: profiles/formatting.md
- Placeholders: profiles/placeholders.md
- API: api.md
- Examples: examples.md
# +--------------------------------------------------------------------------------------------------------+
# | |
# | The "theme" section allows you to define what theme to use. |
# | It is also used for theme-specific options, but also for advanced stuff such as theme-extensions, if |
# | the theme actually supports it. |
# | |
# | Read More: |
# | - https://www.mkdocs.org/user-guide/configuration/#theme |
# | |
# +--------------------------------------------------------------------------------------------------------+
theme:
# +------------------------------------------------------------------------------------------------------+
# | |
# | The "name" option is where you define the theme to use. |
# | |
# | Note that not all themes are included by default and will require you to install them first. |
# | The Material theme is one of them. See the "Read More" link for instructions on how to install it. |
# | |
# | Read More: |
# | - https://squidfunk.github.io/mkdocs-material/getting-started/ |
# | |
# +------------------------------------------------------------------------------------------------------+
name: 'material'
# +------------------------------------------------------------------------------------------------------+
# | |
# | The Material theme allows "theme-extsnions", meaning that you can override parts of it by either |
# | overriding a particular file, or only parts (blocks) of it. |
# | |
# | If you want to override parts of Material, uncomment the "custom_dir" option below and set the |
# | folder (relative to the mkdocs.yml file) where your theme extensions will be located at. |
# | |
# | Read More: |
# | - https://www.mkdocs.org/user-guide/configuration/#custom_dir |
# | - https://squidfunk.github.io/mkdocs-material/customization/#extending-the-theme |
# | |
# +------------------------------------------------------------------------------------------------------+
#custom_dir: 'theme'
# +------------------------------------------------------------------------------------------------------+
# | |
# | The "favicon" option allows you to set your own image/icon to use in the browser-tab. |
# | |
# | Pretty much all image types are supported, but it's recommended to use a PNG, SVG or ICO image for |
# | the favicon. |
# | |
# | The directory is relative to the "docs_dir". |
# | |
# | Example: Having a favicon.png in docs/assets/images will result in the "favicon" setting showing |
# | 'assets/images/favicon.png' |
# | |
# | Read More: |
# | - https://squidfunk.github.io/mkdocs-material/setup/changing-the-logo-and-icons/#favicon |
# | |
# +------------------------------------------------------------------------------------------------------+
favicon: 'assets/images/favicon.png'
logo: 'assets/images/favicon.png'
# +------------------------------------------------------------------------------------------------------+
# | |
# | The "palette" section is a Material option and allows you to define specific style options such as |
# | Color-Scheme, and primary and secondary Color. |
# | |
# | You can also define multiple palettes that can have different Color Schemses and primary and/or |
# | secondary Colors. |
# | |
# | Read More: |
# | - https://squidfunk.github.io/mkdocs-material/setup/changing-the-colors/ |
# | - https://squidfunk.github.io/mkdocs-material/setup/changing-the-colors/#color-palette-toggle |
# | |
# +------------------------------------------------------------------------------------------------------+
palette:
# +----------------------------------------------------------------------------------------------------+
# | |
# | The "scheme" option is used to define the Color Scheme used by Material. |
# | |
# | Material offers a "default" and a "slate" scheme. "default" is obviously the default (bright) |
# | Color scheme while "slate" is the native dark theme from Material. |
# | |
# | Read More: |
# | - https://squidfunk.github.io/mkdocs-material/setup/changing-the-colors/#color-scheme |
# | |
# +----------------------------------------------------------------------------------------------------+
scheme: 'slate'
# +----------------------------------------------------------------------------------------------------+
# | |
# | "primary" is used to set the main color of the Material Theme. |
# | |
# | The primary color is used on the top banner of the page but also for things like the link color. |
# | |
# | Read More: |
# | - https://squidfunk.github.io/mkdocs-material/setup/changing-the-colors/#primary-color |
# | |
# +----------------------------------------------------------------------------------------------------+
primary: 'indigo'
# +----------------------------------------------------------------------------------------------------+
# | |
# | The "accent" is used for moments such as hovering over links. |
# | |
# | Read More: |
# | - https://squidfunk.github.io/mkdocs-material/setup/changing-the-colors/#accent-color |
# | |
# +----------------------------------------------------------------------------------------------------+
accent: 'indigo'
# +------------------------------------------------------------------------------------------------------+
# | |
# | With the "font" option can you set a different font to use. |
# | |
# | Material supports all Google fonts, but you can also define your own ones if you choose so. |
# | |
# | The "text" option is used for the regular font while "code" is used for code blocks, inline code and |
# | similar. |
# | |
# | Read More: |
# | - https://squidfunk.github.io/mkdocs-material/setup/changing-the-fonts/ |
# | |
# +------------------------------------------------------------------------------------------------------+
#font:
# text: 'Roboto'
# code: 'Roboto Mono'
# +------------------------------------------------------------------------------------------------------+
# | |
# | Material suppors more than 40 different languages which you can set using the "language" option |
# | below. |
# | |
# | The default language is "en" (English). |
# | |
# | You can also enable/set a "selector" to allow switching between languages. |
# | See the "alternate" option in the "extra" section below for more information on this topic. |
# | |
# | Read More: |
# | - https://squidfunk.github.io/mkdocs-material/setup/changing-the-language/ |
# | |
# +------------------------------------------------------------------------------------------------------+
#language: 'en'
# +------------------------------------------------------------------------------------------------------+
# | |
# | The "direction" option is commonly used together with the "language" option. |
# | |
# | It allows you to change the text direction from the default left-to-right (ltr) to right-to-left |
# | (rtl) which is used in certain languages. |
# | |
# | Read More: |
# | - https://squidfunk.github.io/mkdocs-material/setup/changing-the-language/#directionality |
# | |
# +------------------------------------------------------------------------------------------------------+
#direction: 'ltr'
# +------------------------------------------------------------------------------------------------------+
# | |
# | The "features" option allows you to enable specific features of Material, by adding them to the |
# | list. |
# | |
# | Features are in the format <category>.<name>. As an example, the feature to enable tabs is called |
# | navigation.tabs. |
# | |
# | The list below contains all known features of Material. |
# | |
# | Features marked with a * are currently Insiders-only. (Last update: 11th December 2021) |
# | https://squidfunk.github.io/mkdocs-material/insiders/ |
# | |
# | Read More: |
# | - https://squidfunk.github.io/mkdocs-material/setup/setting-up-navigation/ |
# | |
# +------------------------------------------------------------------------------------------------------+
features:
# Announce
#
#- announce.dismiss # Adds a "X" button to dismiss a news banner/mark it as read.*
# Header
#
#- header.autohide # Hide header when user scrolls past a specific point.
# Navigation:
#
#- navigation.expand # Expand all collapsable sections.
#- navigation.instant # Instant loading pages.
- navigation.indexes # Attach pages directly to Sections. Incompatible with "toc.integrate"
#- navigation.sections # Render top sections as groups.
- navigation.tabs # Render top sections as tabs at the top.
- navigation.tabs.sticky # Tabs won't disappear when scrolling down. Requires "navigation.tabs".
#- navigation.top # Adds a "Back to top" that is shown when scrolling up.
#- navigation.tracking # Updates the url with highlighted section anchor.
# Search
#
#- search.highlight # Search will highlight the searched word(s) on the page.*
#- search.share # Adds an option to share a search query link.*
#- search.suggest # Search will suggest the likeliest completion for a word.*
# Table of Contents
#
#- toc.integrate # Include the TOC sections in the left navugation.
# +------------------------------------------------------------------------------------------------------+
# | |
# | The "icon" section allows you to define a icon to use for the logo and/or repository. |
# | |
# | To use already available icons will you need to set the right path for it, depending on which you |
# | want to use. |
# | |
# | Available icons: |
# | - FontAwesome |
# | - Brands: fontawesome/brands/... (https://fontawesome.com/icons?d=gallery&p=2&s=brands&m=free) |
# | - Regular: fontawesome/regular/... (https://fontawesome.com/icons?d=gallery&p=2&s=regular&m=free) |
# | - Solid: fontawesome/solid/... (https://fontawesome.com/icons?d=gallery&p=2&s=solid&m=free) |
# | |
# | - Material Design Icons: material/... (https://materialdesignicons.com/) |
# | |
# | - Octicons: octicons/... (https://primer.style/octicons/) |
# | |
# | You can also define your own Image for the logo. To do that, remove the "logo" option from "icon" |
# | instead add a "logo" option on the same level as the "icon" one, where you then set the path |
# | (relative to the "docs_dir") to the icon to use. Supported are all images types, including SVG. |
# | |
# | Read More: |
# | - https://squidfunk.github.io/mkdocs-material/setup/changing-the-logo-and-icons/#logo |
# | |
# +------------------------------------------------------------------------------------------------------+
icon:
repo: 'octicons/mark-github-16'
# +------------------------------------------------------------------------------------------------------+
# | |
# | The "admonition" option allows you to set a different icon for each admonition type. |
# | |
# | This is currently a Insiders-only feature. (Last update: 7th October 2021) |
# | https://squidfunk.github.io/mkdocs-material/insiders/ |
# | |
# | Supported are all bundled icons: |
# | - FontAwesome |
# | - Brands: fontawesome/brands/... (https://fontawesome.com/icons?d=gallery&p=2&s=brands&m=free) |
# | - Regular: fontawesome/regular/... (https://fontawesome.com/icons?d=gallery&p=2&s=regular&m=free) |
# | - Solid: fontawesome/solid/... (https://fontawesome.com/icons?d=gallery&p=2&s=solid&m=free) |
# | |
# | - Material Design Icons: material/... (https://materialdesignicons.com/) |
# | |
# | - Octicons: octicons/... (https://primer.style/octicons/) |
# | |
# | You can also create and use your own icons. See the documentation for more information. |
# | |
# | Read More: |
# | - https://squidfunk.github.io/mkdocs-material/reference/admonitions/#changing-the-icons |
# | |
# +------------------------------------------------------------------------------------------------------+
admonition:
note: 'octicons/tag-16'
abstract: 'octicons/checklist-16'
info: 'octicons/info-16'
tip: 'octicons/squirrel-16'
success: 'octicons/check-16'
question: 'octicons/question-16'
warning: 'octicons/alert-16'
failure: 'octicons/x-circle-16'
danger: 'octicons/zap-16'
bug: 'octicons/bug-16'
example: 'octicons/beaker-16'
quote: 'octicons/quote-16'
# +--------------------------------------------------------------------------------------------------------+
# | |
# | With the "extra_css" option can you add your own (S)CSS files to enhance the documentation. |
# | |
# | The path to the file is relative to the "docs_dir". |
# | |
# | Read More: |
# | - https://www.mkdocs.org/user-guide/configuration/#extra_css |
# | |
# +--------------------------------------------------------------------------------------------------------+
extra_css:
- assets/stylesheets/custom.css
# +--------------------------------------------------------------------------------------------------------+
# | |
# | Similar to the "extra_css" option does the "extra_javascript" option allow you to set custom JS files |
# | to add extra featurues. |
# | |
# | The path to the file is relative to the "docs_dir". |
# | |
# | Read More: |
# | - https://www.mkdocs.org/user-guide/configuration/#extra_javascript |
# | |
# +--------------------------------------------------------------------------------------------------------+
#extra_javascript:
# +--------------------------------------------------------------------------------------------------------+
# | |
# | The "extra" section contains pretty much anything you want, as long as it is a valid key-value pair. |
# | |
# | Material uses this section for different custom settings that wouldn't fit in the theme section. |
# | |
# | Read More: |
# | - https://www.mkdocs.org/user-guide/configuration/#extra |
# | |
# +--------------------------------------------------------------------------------------------------------+
extra:
# +------------------------------------------------------------------------------------------------------+
# | |
# | The social section allows you to set a list of entries which would be displayed in the footer of the |
# | page. |
# | |
# | Each entry has the exact same options: |
# | - icon: Path to the SVG icon to use. See "icon" section for available icon sets. |
# | - link: URL to which the icon should link. |
# | - name: Optional Name that would be displayed as title on hover. |
# | |
# | Read More: |
# | - https://squidfunk.github.io/mkdocs-material/setup/setting-up-the-footer/#social-links |
# | |
# +------------------------------------------------------------------------------------------------------+
social:
- icon: 'fontawesome/brands/github'
link: 'https://github.com/Andre601/asl-wiki'
# +------------------------------------------------------------------------------------------------------+
# | |
# | Allows you to hide the "Made with Material for MkDocs" text in the footer of the pages by setting |
# | this to "true". |
# | |
# | Read More: |
# | - https://squidfunk.github.io/mkdocs-material/setup/setting-up-the-footer/#generator-notice |
# | |
# +------------------------------------------------------------------------------------------------------+
#generator: true
# +------------------------------------------------------------------------------------------------------+
# | |
# | The "manifest" option allows you to define a .manifest file to use. |
# | |
# | A .manifest file makes the doc act like a web-application and tells it how to behave when installed. |
# | |
# | Read More: |
# | - https://squidfunk.github.io/mkdocs-material/reference/meta-tags/#adding-a-web-app-manifest |
# | |
# +------------------------------------------------------------------------------------------------------+
#manifest: manifest.webmanifest
# +------------------------------------------------------------------------------------------------------+
# | |
# | The "alternate" option can be used to create a selector to switch languages. |
# | |
# | Using this requires you to create a specific, more complicated MkDocs setup. |
# | |
# | A Setup Guide for multi-language docs can be found here: |
# | https://github.com/squidfunk/mkdocs-material/discussions/2346 |
# | |
# | Read More: |
# | - https://squidfunk.github.io/mkdocs-material/setup/changing-the-language/#site-language-selector |
# | |
# +------------------------------------------------------------------------------------------------------+
#alternate:
# +--------------------------------------------------------------------------------------------------------+
# | |
# | MkDocs allows the usage of Markdown extensions which can do various things. |
# | |
# | Material includes the pymdownx extension which provides a lot of useful features to use. |
# | |
# | Note that some extensions may use specific settings that you need to set. |
# | Please check out the official documentation of PyMdownx for more information: |
# | https://facelessuser.github.io/pymdown-extensions/ |
# | |
# | Material already provides required CSS and JS values for the PyMdownX Extensions, which means you do |
# | not need to set them up yourself. |
# | |
# | Read More: |
# | - https://www.mkdocs.org/user-guide/configuration/#markdown_extensions |
# | |
# +--------------------------------------------------------------------------------------------------------+
markdown_extensions:
- markdown.extensions.admonition:
- markdown.extensions.toc:
permalink: true
- pymdownx.emoji:
emoji_index: !!python/name:materialx.emoji.twemoji
emoji_generator: !!python/name:materialx.emoji.to_svg
- pymdownx.highlight:
- pymdownx.magiclink:
- pymdownx.superfences:
- pymdownx.tabbed:
alternate_style: true
+11 -7
View File
@@ -2,7 +2,7 @@
<!--
~ MIT License
~
~ Copyright (c) 2022 Andre_601
~ 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
@@ -46,7 +46,7 @@
<repositories>
<repository>
<id>papermc</id>
<url>https://papermc.io/repo/repository/maven-public</url>
<url>https://papermc.io/repo/repository/maven-public/</url>
</repository>
<repository>
<id>placeholderapi-repo</id>
@@ -76,13 +76,13 @@
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>2.11.1</version>
<version>2.11.2</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>AdvancedServerList-PaperMC-${plugin.version}</finalName>
<finalName>AdvancedServerList-Paper-${plugin.version}</finalName>
<resources>
<resource>
<filtering>true</filtering>
@@ -101,9 +101,9 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<version>3.4.1</version>
<configuration>
<minimizeJar>true</minimizeJar>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>org.spongepowered.configurate</pattern>
@@ -111,7 +111,11 @@
</relocation>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>ch.andre601.advancedserverlist.spigot.depends.bstats</shadedPattern>
<shadedPattern>ch.andre601.advancedserverlist.paper.depends.bstats</shadedPattern>
</relocation>
<relocation>
<pattern>com.github.benmanes.caffeine</pattern>
<shadedPattern>ch.andre601.advancedserverlist.paper.depends.caffeine</shadedPattern>
</relocation>
</relocations>
<filters>
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -25,31 +25,60 @@
package ch.andre601.advancedserverlist.paper;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.interfaces.PluginCore;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler;
import ch.andre601.advancedserverlist.paper.commands.CmdAdvancedServerList;
import ch.andre601.advancedserverlist.paper.events.JoinEvent;
import ch.andre601.advancedserverlist.paper.events.PingEvent;
import ch.andre601.advancedserverlist.paper.logging.PaperLogger;
import ch.andre601.advancedserverlist.paper.objects.PaperPlayer;
import ch.andre601.advancedserverlist.paper.objects.PlayerPlaceholders;
import com.destroystokyo.paper.profile.PlayerProfile;
import me.clip.placeholderapi.PlaceholderAPI;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.SimplePie;
import org.bukkit.Bukkit;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.util.CachedServerIcon;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class PaperCore extends JavaPlugin implements PluginCore{
public class PaperCore extends JavaPlugin implements PluginCore<CachedServerIcon, PlayerProfile, PaperPlayer>{
private final PluginLogger logger = new PaperLogger(getLogger());
private AdvancedServerList core;
private FaviconHandler<CachedServerIcon> faviconHandler;
@Override
public void onEnable(){
this.core = new AdvancedServerList(this);
try{
Class.forName("io.papermc.paper.configuration.ConfigurationLoaders");
enable();
}catch(ClassNotFoundException ex){
try{
Class.forName("com.destroystokyo.paper.PaperConfig");
enable();
}catch(ClassNotFoundException ex1){
printSpigotWarning();
getServer().getPluginManager().disablePlugin(this);
}
}
}
@Override
public void onDisable(){
if(getCore() == null)
return; // This is always the case when the plugin is used on a Spigot server (See above try-catch blocks).
getCore().disable();
}
@@ -71,7 +100,17 @@ public class PaperCore extends JavaPlugin implements PluginCore{
@Override
public void loadMetrics(){
new Metrics(this, 15584);
new Metrics(this, 15584).addCustomChart(new SimplePie("profiles",
() -> String.valueOf(core.getFileHandler().getProfiles().size())
));
}
@Override
public void clearFaviconCache(){
if(faviconHandler == null)
return;
faviconHandler.clearCache();
}
@Override
@@ -80,7 +119,7 @@ public class PaperCore extends JavaPlugin implements PluginCore{
}
@Override
public Path getPath(){
public Path getFolderPath(){
return getDataFolder().toPath();
}
@@ -89,6 +128,14 @@ public class PaperCore extends JavaPlugin implements PluginCore{
return logger;
}
@Override
public FaviconHandler<CachedServerIcon> getFaviconHandler(){
if(faviconHandler == null)
faviconHandler = new FaviconHandler<>(core);
return faviconHandler;
}
@Override
public String getPlatformName(){
return getServer().getName();
@@ -98,4 +145,51 @@ public class PaperCore extends JavaPlugin implements PluginCore{
public String getPlatformVersion(){
return getServer().getVersion();
}
@Override
public String getLoader(){
return "paper";
}
@Override
public List<PlayerProfile> createPlayers(List<String> lines, PaperPlayer player, GenericServer server){
List<PlayerProfile> players = new ArrayList<>(lines.size());
for(String line : lines){
String parsed = ComponentParser.text(line)
.applyReplacements(player, server)
.modifyText(text -> {
if(getServer().getPluginManager().isPluginEnabled("PlaceholderAPI"))
return PlaceholderAPI.setPlaceholders(player.getPlayer(), text);
return text;
})
.toString();
players.add(Bukkit.createProfile(UUID.randomUUID(), parsed));
}
return players;
}
private void enable(){
this.core = new AdvancedServerList(this, new PlayerPlaceholders());
}
private void printSpigotWarning(){
getPluginLogger().warn("======================================== WARNING ========================================");
getPluginLogger().warn("");
getPluginLogger().warn("You are using the Paper version of AdvancedServerList on a SpigotMC server.");
getPluginLogger().warn("The Paper version is ONLY compatible with Paper itself due to it using exclusive");
getPluginLogger().warn("methods and events not available within SpigotMC.");
getPluginLogger().warn("");
getPluginLogger().warn("To avoid any exceptions and errors will AdvancedServerList disable itself now.");
getPluginLogger().warn("Please stop your server and either switch to Paper or use the Spigot version");
getPluginLogger().warn("of AdvancedServerList.");
getPluginLogger().warn("");
getPluginLogger().warn("Paper can be downloaded here:");
getPluginLogger().warn("https://papermc.io/downloads");
getPluginLogger().warn("");
getPluginLogger().warn("======================================== WARNING ========================================");
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -26,6 +26,7 @@
package ch.andre601.advancedserverlist.paper.events;
import ch.andre601.advancedserverlist.paper.PaperCore;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
@@ -46,7 +47,8 @@ public class JoinEvent implements Listener{
InetSocketAddress address = event.getPlayer().getAddress();
if(address == null)
return;
plugin.getCore().getPlayerHandler().addPlayer(event.getPlayer().getName(), address.getHostString());
Player player = event.getPlayer();
plugin.getCore().getPlayerHandler().addPlayer(address.getHostString(), player.getName(), player.getUniqueId());
}
}
@@ -0,0 +1,154 @@
/*
* 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.paper.events;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
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.paper.PaperCore;
import ch.andre601.advancedserverlist.paper.objects.PaperPlayer;
import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
import com.destroystokyo.paper.profile.PlayerProfile;
import me.clip.placeholderapi.PlaceholderAPI;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.util.CachedServerIcon;
import java.awt.image.BufferedImage;
import java.util.List;
public class PaperEventWrapper implements GenericEventWrapper<CachedServerIcon, PlayerProfile, PaperPlayer>{
private final PaperCore plugin;
private final PaperServerListPingEvent event;
public PaperEventWrapper(PaperCore plugin, PaperServerListPingEvent event){
this.plugin = plugin;
this.event = event;
}
@Override
public void setMaxPlayers(int maxPlayers){
event.setMaxPlayers(maxPlayers);
}
@Override
public void setMotd(Component component){
event.motd(component);
}
@Override
public void hidePlayers(){
event.setHidePlayers(true);
}
@Override
public void setPlayerCount(String name){
event.setVersion(name);
event.setProtocolVersion(-1);
}
@Override
public void setPlayers(List<String> players, PaperPlayer player, GenericServer server){
event.getPlayerSample().clear();
event.getPlayerSample().addAll(
plugin.createPlayers(players, player, server)
);
}
@Override
public void setFavicon(CachedServerIcon favicon){
event.setServerIcon(favicon);
}
@Override
public void setDefaultFavicon(){
event.setServerIcon(event.getServerIcon());
}
// Not used in Paper
@Override
public void updateEvent(){}
// Not used in Paper
@Override
public boolean isInvalidProtocol(){
return false;
}
@Override
public int getProtocolVersion(){
return event.getClient().getProtocolVersion();
}
@Override
public int getOnlinePlayers(){
return event.getNumPlayers();
}
@Override
public int getMaxPlayers(){
return event.getMaxPlayers();
}
@Override
public String getPlayerIP(){
return event.getClient().getAddress().getHostString();
}
@Override
public String parsePAPIPlaceholders(String text, PaperPlayer player){
if(plugin.getServer().getPluginManager().isPluginEnabled("PlaceholderAPI"))
return PlaceholderAPI.setPlaceholders(player.getPlayer(), text);
return text;
}
@Override
public String getVirtualHost(){
return this.resolveHost(event.getClient().getVirtualHost());
}
@Override
public PluginCore<CachedServerIcon, PlayerProfile, PaperPlayer> getPlugin(){
return plugin;
}
@Override
public PaperPlayer createPlayer(CachedPlayer player, int protocol){
OfflinePlayer pl = Bukkit.getOfflinePlayer(player.getUuid());
return new PaperPlayer(pl.hasPlayedBefore() ? pl : null, player, protocol);
}
@Override
public CachedServerIcon createFavicon(BufferedImage image) throws Exception{
return Bukkit.loadServerIcon(image);
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -25,24 +25,12 @@
package ch.andre601.advancedserverlist.paper.events;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import ch.andre601.advancedserverlist.core.profiles.ProfileManager;
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
import ch.andre601.advancedserverlist.core.events.PingEventHandler;
import ch.andre601.advancedserverlist.paper.PaperCore;
import com.destroystokyo.paper.event.server.PaperServerListPingEvent;
import com.destroystokyo.paper.profile.PlayerProfile;
import me.clip.placeholderapi.PlaceholderAPI;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class PingEvent implements Listener{
private final PaperCore plugin;
@@ -54,82 +42,6 @@ public class PingEvent implements Listener{
@EventHandler
public void onServerPing(PaperServerListPingEvent event){
InetSocketAddress address = event.getClient().getAddress();
Map<String, Object> replacements = plugin.getCore()
.loadPlaceholders(event.getClient().getProtocolVersion(), event.getNumPlayers(), event.getMaxPlayers(), address);
ServerListProfile profile = ProfileManager.get(plugin.getCore())
.replacements(replacements)
.getProfile();
if(profile == null)
return;
OfflinePlayer player = resolvePlayer(address);
if(!profile.getMotd().isEmpty()){
event.motd(ComponentParser.list(profile.getMotd())
.replacements(replacements)
.function(text -> {
if(plugin.getServer().getPluginManager().isPluginEnabled("PlaceholderAPI"))
return PlaceholderAPI.setPlaceholders(player, text);
return text;
})
.toComponent());
}
if(!profile.getPlayerCount().isEmpty()){
event.setVersion(ComponentParser.text(profile.getPlayerCount())
.replacements(replacements)
.function(text -> {
if(plugin.getServer().getPluginManager().isPluginEnabled("PlaceholderAPI"))
return PlaceholderAPI.setPlaceholders(player, text);
return text;
})
.toString());
event.setProtocolVersion(-1);
}
if(!profile.getPlayers().isEmpty()){
event.getPlayerSample().clear();
event.getPlayerSample().addAll(getPlayers(profile.getPlayers(), replacements, player));
}
event.setServerIcon(event.getServerIcon());
}
private List<PlayerProfile> getPlayers(List<String> lines, Map<String, Object> replacements, OfflinePlayer player){
List<PlayerProfile> players = new ArrayList<>();
lines.forEach(line -> players.add(Bukkit.createProfile(UUID.randomUUID(), ComponentParser.text(line)
.replacements(replacements)
.function(text -> {
plugin.getPluginLogger().info("Before: " + text);
if(plugin.getServer().getPluginManager().isPluginEnabled("PlaceholderAPI")){
String after = PlaceholderAPI.setPlaceholders(player, text);
plugin.getPluginLogger().info("After: " + after);
return after;
}
return text;
})
.toString())));
return players;
}
private OfflinePlayer resolvePlayer(InetSocketAddress address){
String playerName = plugin.getCore().getPlayerHandler().getPlayerByIp(address.getHostString());
OfflinePlayer player = Bukkit.getPlayerExact(playerName);
if(player == null){
player = Bukkit.getOfflinePlayer(playerName);
return player.hasPlayedBefore() ? player : null;
}
return player;
PingEventHandler.handleEvent(new PaperEventWrapper(plugin, event));
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -39,17 +39,22 @@ public class PaperLogger implements PluginLogger{
}
@Override
public void info(String msg){
logger.info(msg);
public void info(String msg, Object... args){
logger.info(String.format(msg, args));
}
@Override
public void warn(String msg){
logger.warning(msg);
public void warn(String msg, Object... args){
logger.warning(String.format(msg, args));
}
@Override
public void warn(String msg, Throwable throwable){
logger.log(Level.WARNING, msg, throwable);
}
@Override
public void warn(String msg, Throwable throwable, Object... args){
logger.log(Level.WARNING, String.format(msg, args), throwable);
}
}
@@ -0,0 +1,54 @@
/*
* 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.paper.objects;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.core.objects.CachedPlayer;
import org.bukkit.OfflinePlayer;
public class PaperPlayer extends GenericPlayer{
private final OfflinePlayer player;
public PaperPlayer(OfflinePlayer player, CachedPlayer cachedPlayer, int protocol){
this.player = player;
this.name = player == null ? cachedPlayer.getName() : player.getName();
this.protocol = protocol;
this.uuid = player == null ? cachedPlayer.getUuid() : player.getUniqueId();
if(player == null)
return;
this.playedBefore = player.hasPlayedBefore();
this.banned = player.isBanned();
this.whitelisted = player.isWhitelisted();
}
public OfflinePlayer getPlayer(){
return player;
}
}
@@ -0,0 +1,56 @@
/*
* 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.paper.objects;
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
public class PlayerPlaceholders extends PlaceholderProvider{
public PlayerPlaceholders(){
super("player");
}
@Override
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
PaperPlayer paperPlayer = (PaperPlayer)player;
return switch(placeholder){
case "name" -> paperPlayer.getName();
case "protocol" -> String.valueOf(paperPlayer.getProtocol());
case "uuid" -> String.valueOf(paperPlayer.getUUID());
case "hasPlayedBefore" -> returnValue(paperPlayer, player.hasPlayedBefore());
case "isBanned" -> returnValue(paperPlayer, player.isBanned());
case "isWhitelisted" -> returnValue(paperPlayer, player.isWhitelisted());
default -> null;
};
}
private String returnValue(PaperPlayer player, Object value){
return player.getPlayer() == null ? null : String.valueOf(value);
}
}
+4 -1
View File
@@ -7,13 +7,16 @@ main: 'ch.andre601.advancedserverlist.paper.PaperCore'
api-version: 1.19
libraries:
- 'com.squareup.okhttp3:okhttp:4.10.0'
softdepend:
- 'PlaceholderAPI'
commands:
advancedserverlist:
description: 'Main command of AdvancedServerList'
usage: '/<command> [reload]'
usage: '/<command> [help|reload]'
aliases:
- 'asl'
permission: 'advancedserverlist.admin'
+28 -14
View File
@@ -1,4 +1,29 @@
<?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">
@@ -12,30 +37,19 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<plugin.version>1.0.1</plugin.version>
<plugin.version>2.0.0</plugin.version>
<plugin.description>Create multiple Server lists based on conditions.</plugin.description>
<plugin.depend.mc>1.19-R0.1-SNAPSHOT</plugin.depend.mc>
<plugin.depend.velocity>3.1.1-SNAPSHOT</plugin.depend.velocity>
<plugin.depend.configurate>4.1.2</plugin.depend.configurate>
<plugin.depend.kyori-adventure>4.11.0</plugin.depend.kyori-adventure>
<plugin.depend.kyori-minimessage>4.11.0</plugin.depend.kyori-minimessage>
<plugin.depend.kyori-legacy>4.11.0</plugin.depend.kyori-legacy>
<plugin.depend.kyori-platform-bungeecord>4.1.0</plugin.depend.kyori-platform-bungeecord>
<plugin.depend.kyori-platform-bukkit>4.1.1</plugin.depend.kyori-platform-bukkit>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
</properties>
<modules>
<module>api</module>
<module>core</module>
<module>velocity</module>
<module>bungeecord</module>
<module>paper</module>
<module>spigot</module>
<module>velocity</module>
</modules>
</project>
+1
View File
@@ -0,0 +1 @@
mkdocs-material==9.0.6
+11 -7
View File
@@ -2,7 +2,7 @@
<!--
~ MIT License
~
~ Copyright (c) 2022 Andre_601
~ 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
@@ -68,13 +68,13 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>${plugin.depend.mc}</version>
<version>1.19-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-platform-bukkit</artifactId>
<version>${plugin.depend.kyori-platform-bukkit}</version>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>com.comphenix.protocol</groupId>
@@ -91,7 +91,7 @@
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>2.11.1</version>
<version>2.11.2</version>
<scope>provided</scope>
</dependency>
</dependencies>
@@ -116,13 +116,17 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<version>3.4.0</version>
<configuration>
<minimizeJar>true</minimizeJar>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>ch.andre601.advancedserverlist.paper.depends.bstats</shadedPattern>
<shadedPattern>ch.andre601.advancedserverlist.spigot.depends.bstats</shadedPattern>
</relocation>
<relocation>
<pattern>com.github.benmanes.caffeine</pattern>
<shadedPattern>ch.andre601.advancedserverlist.spigot.depends.caffeine</shadedPattern>
</relocation>
</relocations>
<filters>
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -25,25 +25,36 @@
package ch.andre601.advancedserverlist.spigot;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.interfaces.PluginCore;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler;
import ch.andre601.advancedserverlist.spigot.commands.CmdAdvancedServerList;
import ch.andre601.advancedserverlist.spigot.events.LoadEvent;
import ch.andre601.advancedserverlist.spigot.logging.SpigotLogger;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import ch.andre601.advancedserverlist.spigot.objects.PlayerPlaceholders;
import ch.andre601.advancedserverlist.spigot.objects.SpigotPlayer;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedServerPing;
import me.clip.placeholderapi.PlaceholderAPI;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.SimplePie;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.java.JavaPlugin;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class SpigotCore extends JavaPlugin implements PluginCore{
public class SpigotCore extends JavaPlugin implements PluginCore<WrappedServerPing.CompressedImage, WrappedGameProfile, SpigotPlayer>{
private final PluginLogger logger = new SpigotLogger(getLogger());
private AdvancedServerList core;
private ProtocolManager protocolManager;
private final PluginLogger logger = new SpigotLogger(getLogger());
FaviconHandler<WrappedServerPing.CompressedImage> faviconHandler = null;
@Override
public void onEnable(){
@@ -60,9 +71,7 @@ public class SpigotCore extends JavaPlugin implements PluginCore{
}catch(ClassNotFoundException ignored){}
}
protocolManager = ProtocolLibrary.getProtocolManager();
this.core = new AdvancedServerList(this);
this.core = new AdvancedServerList(this, new PlayerPlaceholders());
}
@Override
@@ -87,7 +96,17 @@ public class SpigotCore extends JavaPlugin implements PluginCore{
@Override
public void loadMetrics(){
new Metrics(this, 15584);
new Metrics(this, 15584).addCustomChart(new SimplePie("profiles",
() -> String.valueOf(core.getFileHandler().getProfiles().size())
));
}
@Override
public void clearFaviconCache(){
if(faviconHandler == null)
return;
faviconHandler.clearCache();
}
@Override
@@ -96,7 +115,7 @@ public class SpigotCore extends JavaPlugin implements PluginCore{
}
@Override
public Path getPath(){
public Path getFolderPath(){
return getDataFolder().toPath();
}
@@ -105,6 +124,14 @@ public class SpigotCore extends JavaPlugin implements PluginCore{
return logger;
}
@Override
public FaviconHandler<WrappedServerPing.CompressedImage> getFaviconHandler(){
if(faviconHandler == null)
faviconHandler = new FaviconHandler<>(core);
return faviconHandler;
}
@Override
public String getPlatformName(){
return getServer().getName();
@@ -115,13 +142,41 @@ public class SpigotCore extends JavaPlugin implements PluginCore{
return getServer().getVersion();
}
@Override
public String getLoader(){
return "spigot";
}
@Override
public List<WrappedGameProfile> createPlayers(List<String> lines, SpigotPlayer player, GenericServer server){
List<WrappedGameProfile> players = new ArrayList<>(lines.size());
for(String line : lines){
String parsed = ComponentParser.text(line)
.applyReplacements(player, server)
.modifyText(text -> {
if(getServer().getPluginManager().isPluginEnabled("PlaceholderAPI"))
return PlaceholderAPI.setPlaceholders(player.getPlayer(), text);
return text;
})
.toString();
players.add(new WrappedGameProfile(UUID.randomUUID(), parsed));
}
return players;
}
private void printPaperInfo(){
getPluginLogger().warn("You are using the Spigot version of AdvancedServerList on a PaperMC server.");
getPluginLogger().warn("It is recommended to use the dedicated PaperMC version, to benefit from the");
getPluginLogger().warn("Following improvements:");
getPluginLogger().warn("- No need to download external libraries already provided by PaperMC.");
getPluginLogger().warn("- No dependency on ProtocolLib thanks to provided Events.");
getPluginLogger().warn("======================================================================================");
getPluginLogger().warn("You are using the Spigot version of AdvancedServerList on a Paper server.");
getPluginLogger().warn("It is recommended to use the dedicated Paper version, to benefit from the");
getPluginLogger().warn("following improvements:");
getPluginLogger().warn(" - No need to download external libraries already provided by PaperMC.");
getPluginLogger().warn(" - No dependency on ProtocolLib thanks to provided Events.");
getPluginLogger().warn("");
getPluginLogger().warn("AdvancedServerList may work as normal, but consider using the PaperMC version instead!");
getPluginLogger().warn("======================================================================================");
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -26,6 +26,7 @@
package ch.andre601.advancedserverlist.spigot.events;
import ch.andre601.advancedserverlist.spigot.SpigotCore;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
@@ -45,7 +46,8 @@ public class JoinEvent implements Listener{
InetSocketAddress address = event.getPlayer().getAddress();
if(address == null)
return;
plugin.getCore().getPlayerHandler().addPlayer(event.getPlayer().getName(), address.getHostString());
Player player = event.getPlayer();
plugin.getCore().getPlayerHandler().addPlayer(address.getHostString(), player.getName(), player.getUniqueId());
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -46,7 +46,7 @@ public class LoadEvent implements Listener{
PluginManager manager = plugin.getServer().getPluginManager();
if(!manager.isPluginEnabled("ProtocolLib")){
plugin.getCore().getPluginLogger().warn("ProtocolLib not found! AdvancedServerList requires it to work on Spigot!");
plugin.getCore().getPlugin().getPluginLogger().warn("ProtocolLib not found! AdvancedServerList requires it to work on Spigot!");
manager.disablePlugin(plugin);
return;
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -25,124 +25,50 @@
package ch.andre601.advancedserverlist.spigot.events;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import ch.andre601.advancedserverlist.core.profiles.ProfileManager;
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
import ch.andre601.advancedserverlist.core.events.PingEventHandler;
import ch.andre601.advancedserverlist.spigot.SpigotCore;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedServerPing;
import me.clip.placeholderapi.PlaceholderAPI;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.event.Listener;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.HashMap;
import java.util.Map;
public class PingEvent implements Listener{
private final SpigotCore plugin;
private final ProtocolManager protocolManager;
private final Map<String, String> hostAddresses = new HashMap<>();
public PingEvent(SpigotCore plugin, ProtocolManager protocolManager){
this.plugin = plugin;
this.protocolManager = protocolManager;
loadPacketListener(plugin);
}
private void loadPacketListener(SpigotCore spigotPlugin){
protocolManager.addPacketListener(new PacketAdapter(spigotPlugin, ListenerPriority.LOW, PacketType.Status.Server.SERVER_INFO){
protocolManager.addPacketListener(new PacketAdapter(spigotPlugin, ListenerPriority.LOW, PacketType.Handshake.Client.SET_PROTOCOL){
@Override
public void onPacketSending(PacketEvent event){
WrappedServerPing ping = event.getPacket().getServerPings().read(0);
public void onPacketReceiving(PacketEvent event){
InetSocketAddress address = event.getPlayer().getAddress();
if(address == null)
return;
Map<String, Object> replacements = spigotPlugin.getCore()
.loadPlaceholders(ping.getVersionProtocol(), ping.getPlayersOnline(), ping.getPlayersMaximum(), address);
String host = event.getPacket().getStrings().read(0);
ServerListProfile profile = ProfileManager.get(spigotPlugin.getCore())
.replacements(replacements)
.getProfile();
if(profile == null)
return;
OfflinePlayer player = resolvePlayer(address);
if(!profile.getMotd().isEmpty()){
ping.setMotD(ComponentParser.list(profile.getMotd())
.replacements(replacements)
.function(text -> {
if(plugin.getServer().getPluginManager().isPluginEnabled("PlaceholderAPI"))
return PlaceholderAPI.setPlaceholders(player, text);
return text;
})
.toString()
);
}
if(!profile.getPlayerCount().isEmpty()){
ping.setVersionName(ComponentParser.text(profile.getPlayerCount())
.replacements(replacements)
.function(text -> {
if(plugin.getServer().getPluginManager().isPluginEnabled("PlaceholderAPI"))
return PlaceholderAPI.setPlaceholders(player, text);
return text;
})
.toString()
);
ping.setVersionProtocol(-1);
}
if(!profile.getPlayers().isEmpty()){
ping.setPlayers(getFakePlayers(
ComponentParser.list(profile.getPlayers())
.replacements(replacements)
.function(text -> {
if(plugin.getServer().getPluginManager().isPluginEnabled("PlaceholderAPI"))
return PlaceholderAPI.setPlaceholders(player, text);
return text;
})
.toString()
));
}
hostAddresses.put(address.getHostString(), host);
}
});
protocolManager.addPacketListener(new PacketAdapter(spigotPlugin, ListenerPriority.LOW, PacketType.Status.Server.SERVER_INFO){
@Override
public void onPacketSending(PacketEvent event){
PingEventHandler.handleEvent(new ProtocolLibEventWrapper(spigotPlugin, event, hostAddresses));
}
});
}
private List<WrappedGameProfile> getFakePlayers(String text){
String[] lines = text.split("\n");
List<WrappedGameProfile> profiles = new ArrayList<>();
for(String line : lines){
profiles.add(new WrappedGameProfile(UUID.randomUUID(), line));
}
return profiles;
}
private OfflinePlayer resolvePlayer(InetSocketAddress address){
String playerName = plugin.getCore().getPlayerHandler().getPlayerByIp(address.getHostString());
OfflinePlayer player = Bukkit.getPlayerExact(playerName);
if(player == null){
//noinspection deprecation
player = Bukkit.getOfflinePlayer(playerName);
return player.hasPlayedBefore() ? player : null;
}
return player;
}
}
@@ -0,0 +1,163 @@
/*
* 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.spigot.events;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
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.spigot.SpigotCore;
import ch.andre601.advancedserverlist.spigot.objects.SpigotPlayer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.wrappers.AdventureComponentConverter;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedServerPing;
import me.clip.placeholderapi.PlaceholderAPI;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import java.awt.image.BufferedImage;
import java.util.List;
import java.util.Map;
public class ProtocolLibEventWrapper implements GenericEventWrapper<WrappedServerPing.CompressedImage, WrappedGameProfile, SpigotPlayer>{
private final SpigotCore plugin;
private final PacketEvent event;
private final WrappedServerPing ping;
private final Map<String, String> hostAddresses;
public ProtocolLibEventWrapper(SpigotCore plugin, PacketEvent event, Map<String, String> hostAddresses){
this.plugin = plugin;
this.event = event;
this.ping = event.getPacket().getServerPings().read(0);
this.hostAddresses = hostAddresses;
}
@Override
public void setMaxPlayers(int maxPlayers){
ping.setPlayersMaximum(maxPlayers);
}
@Override
public void setMotd(Component component){
ping.setMotD(AdventureComponentConverter.fromComponent(component));
}
@Override
public void hidePlayers(){
ping.setPlayersVisible(false);
}
@Override
public void setPlayerCount(String name){
ping.setVersionName(name);
ping.setVersionProtocol(-1);
}
@Override
public void setPlayers(List<String> players, SpigotPlayer player, GenericServer server){
ping.setPlayers(
plugin.createPlayers(players, player, server)
);
}
@Override
public void setFavicon(WrappedServerPing.CompressedImage favicon){
ping.setFavicon(favicon);
}
@Override
public void setDefaultFavicon(){
ping.setFavicon(ping.getFavicon());
}
// Not used in ProtocolLib
@Override
public void updateEvent(){}
@Override
public boolean isInvalidProtocol(){
return event.getPlayer().getAddress() == null;
}
@Override
public int getProtocolVersion(){
return ping.getVersionProtocol();
}
@Override
public int getOnlinePlayers(){
return ping.getPlayersOnline();
}
@Override
public int getMaxPlayers(){
return ping.getPlayersMaximum();
}
@Override
public String getPlayerIP(){
return event.getPlayer().getAddress() == null ? "UNKNOWN" : event.getPlayer().getAddress().getHostString();
}
@Override
public String parsePAPIPlaceholders(String text, SpigotPlayer player){
if(plugin.getServer().getPluginManager().isPluginEnabled("PlaceholderAPI"))
return PlaceholderAPI.setPlaceholders(player.getPlayer(), text);
return text;
}
@Override
public String getVirtualHost(){
String host = this.resolveHost(event.getPlayer().getAddress());
if(host == null)
return null;
return hostAddresses.get(host);
}
@Override
public PluginCore<WrappedServerPing.CompressedImage, WrappedGameProfile, SpigotPlayer> getPlugin(){
return plugin;
}
@Override
public SpigotPlayer createPlayer(CachedPlayer player, int protocol){
OfflinePlayer pl = Bukkit.getOfflinePlayer(player.getUuid());
return new SpigotPlayer(pl.hasPlayedBefore() ? pl : null, player, protocol);
}
@Override
public WrappedServerPing.CompressedImage createFavicon(BufferedImage image) throws Exception{
return WrappedServerPing.CompressedImage.fromPng(image);
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -39,17 +39,22 @@ public class SpigotLogger implements PluginLogger{
}
@Override
public void info(String msg){
logger.info(msg);
public void info(String msg, Object... args){
logger.info(String.format(msg, args));
}
@Override
public void warn(String msg){
logger.warning(msg);
public void warn(String msg, Object... args){
logger.warning(String.format(msg, args));
}
@Override
public void warn(String msg, Throwable throwable){
logger.log(Level.WARNING, msg, throwable);
}
@Override
public void warn(String msg, Throwable throwable, Object... args){
logger.log(Level.WARNING, String.format(msg, args), throwable);
}
}
@@ -0,0 +1,56 @@
/*
* 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.spigot.objects;
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
public class PlayerPlaceholders extends PlaceholderProvider{
public PlayerPlaceholders(){
super("player");
}
@Override
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
SpigotPlayer spigotPlayer = (SpigotPlayer)player;
return switch(placeholder){
case "name" -> spigotPlayer.getName();
case "protocol" -> String.valueOf(spigotPlayer.getProtocol());
case "uuid" -> String.valueOf(spigotPlayer.getUUID());
case "hasPlayedBefore" -> returnValue(spigotPlayer, player.hasPlayedBefore());
case "isBanned" -> returnValue(spigotPlayer, player.isBanned());
case "isWhitelisted" -> returnValue(spigotPlayer, player.isWhitelisted());
default -> null;
};
}
private String returnValue(SpigotPlayer player, Object value){
return player.getPlayer() == null ? null : String.valueOf(value);
}
}
@@ -0,0 +1,55 @@
/*
* 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.spigot.objects;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.core.objects.CachedPlayer;
import org.bukkit.OfflinePlayer;
public class SpigotPlayer extends GenericPlayer{
private final OfflinePlayer player;
public SpigotPlayer(OfflinePlayer player, CachedPlayer cachedPlayer, int protocol){
this.player = player;
this.name = player == null ? cachedPlayer.getName() : player.getName();
this.protocol = protocol;
this.uuid = player == null ? cachedPlayer.getUuid() : player.getUniqueId();
if(player == null)
return;
this.playedBefore = player.hasPlayedBefore();
this.banned = player.isBanned();
this.whitelisted = player.isWhitelisted();
}
public OfflinePlayer getPlayer(){
return player;
}
}
+7 -6
View File
@@ -8,11 +8,12 @@ main: 'ch.andre601.advancedserverlist.spigot.SpigotCore'
api-version: 1.19
libraries:
- 'org.spongepowered:configurate-yaml:${plugin.depend.configurate}'
- 'net.kyori:adventure-api:${plugin.depend.kyori-adventure}'
- 'net.kyori:adventure-text-minimessage:${plugin.depend.kyori-minimessage}'
- 'net.kyori:adventure-text-serializer-legacy:${plugin.depend.kyori-legacy}'
- 'net.kyori:adventure-platform-bukkit:${plugin.depend.kyori-platform-bukkit}'
- 'org.spongepowered:configurate-yaml:4.1.2'
- 'net.kyori:adventure-api:4.12.0'
- 'net.kyori:adventure-text-minimessage:4.12.0'
- 'net.kyori:adventure-text-serializer-legacy:4.12.0'
- 'net.kyori:adventure-platform-bukkit:4.2.0'
- 'com.squareup.okhttp3:okhttp:4.10.0'
softdepend:
- 'ProtocolLib'
@@ -21,7 +22,7 @@ softdepend:
commands:
advancedserverlist:
description: 'Main command of AdvancedServerList'
usage: '/<command> [reload]'
usage: '/<command> [help|reload]'
aliases:
- 'asl'
permission: 'advancedserverlist.admin'
+13 -3
View File
@@ -2,7 +2,7 @@
<!--
~ MIT License
~
~ Copyright (c) 2022 Andre_601
~ 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
@@ -35,6 +35,8 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>velocity</artifactId>
<packaging>jar</packaging>
<version>${plugin.version}</version>
<properties>
<maven.compiler.source>16</maven.compiler.source>
@@ -86,9 +88,9 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<version>3.4.1</version>
<configuration>
<minimizeJar>true</minimizeJar>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>org.spongepowered.configurate</pattern>
@@ -98,6 +100,14 @@
<pattern>org.bstats</pattern>
<shadedPattern>ch.andre601.advancedserverlist.velocity.depends.bstats</shadedPattern>
</relocation>
<relocation>
<pattern>com.github.benmanes.caffeine</pattern>
<shadedPattern>ch.andre601.advancedserverlist.velocity.depends.caffeine</shadedPattern>
</relocation>
<relocation>
<pattern>okhttp3</pattern>
<shadedPattern>ch.andre601.advancedserverlist.velocity.depends.okhttp3</shadedPattern>
</relocation>
</relocations>
<filters>
<filter>
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -25,13 +25,18 @@
package ch.andre601.advancedserverlist.velocity;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.interfaces.PluginCore;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.interfaces.core.PluginCore;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import ch.andre601.advancedserverlist.core.profiles.favicon.FaviconHandler;
import ch.andre601.advancedserverlist.velocity.commands.CmdAdvancedServerList;
import ch.andre601.advancedserverlist.velocity.events.JoinEvent;
import ch.andre601.advancedserverlist.velocity.events.PingEvent;
import ch.andre601.advancedserverlist.velocity.logging.VelocityLogger;
import ch.andre601.advancedserverlist.velocity.objects.PlayerPlaceholders;
import ch.andre601.advancedserverlist.velocity.objects.VelocityPlayer;
import com.google.inject.Inject;
import com.velocitypowered.api.command.CommandMeta;
import com.velocitypowered.api.event.Subscribe;
@@ -39,12 +44,18 @@ import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.server.ServerPing;
import com.velocitypowered.api.util.Favicon;
import org.bstats.charts.SimplePie;
import org.bstats.velocity.Metrics;
import org.slf4j.LoggerFactory;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class VelocityCore implements PluginCore{
public class VelocityCore implements PluginCore<Favicon, ServerPing.SamplePlayer, VelocityPlayer>{
private final PluginLogger logger;
private final ProxyServer proxy;
@@ -52,6 +63,7 @@ public class VelocityCore implements PluginCore{
private final Metrics.Factory metrics;
private AdvancedServerList core;
private FaviconHandler<Favicon> faviconHandler = null;
@Inject
public VelocityCore(ProxyServer proxy, @DataDirectory Path path, Metrics.Factory metrics){
@@ -64,7 +76,7 @@ public class VelocityCore implements PluginCore{
@Subscribe
public void init(ProxyInitializeEvent event){
this.core = new AdvancedServerList(this);
this.core = new AdvancedServerList(this, new PlayerPlaceholders());
}
@Subscribe
@@ -90,7 +102,17 @@ public class VelocityCore implements PluginCore{
@Override
public void loadMetrics(){
metrics.make(this, 15587);
metrics.make(this, 15587).addCustomChart(new SimplePie("profiles",
() -> String.valueOf(core.getFileHandler().getProfiles().size())
));
}
@Override
public void clearFaviconCache(){
if(faviconHandler == null)
return;
faviconHandler.clearCache();
}
@Override
@@ -99,7 +121,7 @@ public class VelocityCore implements PluginCore{
}
@Override
public Path getPath(){
public Path getFolderPath(){
return path;
}
@@ -108,6 +130,29 @@ public class VelocityCore implements PluginCore{
return logger;
}
@Override
public FaviconHandler<Favicon> getFaviconHandler(){
if(faviconHandler == null)
faviconHandler = new FaviconHandler<>(core);
return faviconHandler;
}
@Override
public List<ServerPing.SamplePlayer> createPlayers(List<String> lines, VelocityPlayer player, GenericServer server){
List<ServerPing.SamplePlayer> players = new ArrayList<>(lines.size());
for(String line : lines){
String parsed = ComponentParser.text(line)
.applyReplacements(player, server)
.toString();
players.add(new ServerPing.SamplePlayer(parsed, UUID.randomUUID()));
}
return players;
}
@Override
public String getPlatformName(){
return getProxy().getVersion().getName();
@@ -118,6 +163,11 @@ public class VelocityCore implements PluginCore{
return getProxy().getVersion().getVersion();
}
@Override
public String getLoader(){
return "velocity";
}
public ProxyServer getProxy(){
return proxy;
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -28,6 +28,7 @@ package ch.andre601.advancedserverlist.velocity.events;
import ch.andre601.advancedserverlist.velocity.VelocityCore;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.PostLoginEvent;
import com.velocitypowered.api.proxy.Player;
import java.net.InetSocketAddress;
@@ -43,6 +44,8 @@ public class JoinEvent{
@Subscribe
public void onJoin(PostLoginEvent event){
InetSocketAddress address = event.getPlayer().getRemoteAddress();
plugin.getCore().getPlayerHandler().addPlayer(event.getPlayer().getUsername(), address.getHostString());
Player player = event.getPlayer();
plugin.getCore().getPlayerHandler().addPlayer(address.getHostString(), player.getUsername(), player.getUniqueId());
}
}
@@ -1,7 +1,7 @@
/*
* MIT License
*
* Copyright (c) 2022 Andre_601
* 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
@@ -25,21 +25,11 @@
package ch.andre601.advancedserverlist.velocity.events;
import ch.andre601.advancedserverlist.core.AdvancedServerList;
import ch.andre601.advancedserverlist.core.parsing.ComponentParser;
import ch.andre601.advancedserverlist.core.profiles.ProfileManager;
import ch.andre601.advancedserverlist.core.profiles.ServerListProfile;
import ch.andre601.advancedserverlist.core.profiles.replacer.Placeholders;
import ch.andre601.advancedserverlist.core.events.PingEventHandler;
import ch.andre601.advancedserverlist.velocity.VelocityCore;
import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.proxy.ProxyPingEvent;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.server.ServerPing;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.Map;
public class PingEvent{
@@ -52,53 +42,6 @@ public class PingEvent{
@Subscribe(order = PostOrder.EARLY)
public void onProxyPing(ProxyPingEvent event){
ServerPing ping = event.getPing();
ServerPing.Version protocol = ping.getVersion();
if(protocol == null)
return;
ServerPing.Builder builder = ping.asBuilder();
InetSocketAddress address = event.getConnection().getRemoteAddress();
Map<String, Object> replacements = plugin.getCore()
.loadPlaceholders(protocol.getProtocol(), builder.getOnlinePlayers(), builder.getMaximumPlayers(), address);
replacements.put(Placeholders.PLAYER_VERSION, ProtocolVersion.getProtocolVersion(protocol.getProtocol()));
ServerListProfile profile = ProfileManager.get(plugin.getCore())
.replacements(replacements)
.getProfile();
if(profile == null)
return;
if(!profile.getMotd().isEmpty()){
List<String> motd = profile.getMotd();
builder.description(ComponentParser.list(motd)
.replacements(replacements)
.toComponent()
);
}
if(!profile.getPlayerCount().isEmpty()){
builder.version(new ServerPing.Version(
-1,
ComponentParser.text(profile.getPlayerCount())
.replacements(replacements)
.toString()
));
}
if(!profile.getPlayers().isEmpty()){
String players = ComponentParser.list(profile.getPlayers())
.replacements(replacements)
.toString();
ServerPing.SamplePlayer[] playerSamples = AdvancedServerList.getPlayers(ServerPing.SamplePlayer.class, players)
.toArray(new ServerPing.SamplePlayer[0]);
if(playerSamples.length > 0)
builder.clearSamplePlayers().samplePlayers(playerSamples);
}
event.setPing(builder.build());
PingEventHandler.handleEvent(new VelocityEventWrapper(plugin, event));
}
}
Loaded 100 of 104 files, more files were not shown because too many files have changed in this diff. Show more