Compare commits

...
63 Commits
Author SHA1 Message Date
Andre601 6e5466aacf Use relativePath for pom.xml resolving 2024-02-18 23:47:09 +01:00
Andre601 e6635cd844 Fix wrong add-on name 2024-02-18 23:37:38 +01:00
Andre601 229ab60c57 Add ExpressionsWarnHelper for (hopefully) better warnings. 2024-02-18 23:23:06 +01:00
Andre601 483daee999 Merge pull request 'v4.0.0: AdvancedBan Addon' (#222) from feature/advanced-ban-addon into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/222
2024-02-18 20:45:42 +00:00
Andre601 31ffcd7f7b Add placeholders for mute and ban expiration dates 2024-02-18 17:44:16 +01:00
Andre601 e5139cc00d Slightly better wording for loaded placeholder sets 2024-02-18 15:35:55 +01:00
Andre601 9b1b95b27a Merge branch 'master' into feature/advanced-ban-addon 2024-02-18 14:33:37 +00:00
Andre601 88d3ceeaf3 Use PlatformInfo to store add-ons paths 2024-02-18 15:33:11 +01:00
Andre601 f74f2f337c Only supports 1.20+ on Paper 2024-02-18 15:22:53 +01:00
Andre601 87b35f240e Make the addon more generic and work on Paper too 2024-02-18 15:20:58 +01:00
Andre601 fc861ea304 Merge pull request 'Update api.version to v3.2.0' (#224) from renovate/api.version into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/224
2024-02-18 12:29:57 +00:00
Andre601's Renovate Bot 927971ca12 Update api.version to v3.2.0 2024-02-18 12:01:52 +00:00
Andre601 0ecb8383e3 Make the AdvancedBan Addon actually work. 2024-02-17 16:56:04 +01:00
Andre601 9c3b2220d5 Add some debug messages 2024-02-17 14:13:16 +01:00
Andre601 46e15441fd Merge pull request 'Update dependency ch.qos.logback:logback-classic to v1.5.0' (#223) from renovate/ch.qos.logback-logback-classic-1.x into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/223
2024-02-17 12:09:19 +00:00
Andre601's Renovate Bot 2d2a1ca805 Update dependency ch.qos.logback:logback-classic to v1.5.0 2024-02-17 12:01:07 +00:00
Andre601 c577264e1d Update documentation 2024-02-16 11:13:50 +01:00
Andre601 27c3972ab0 Bump to v4.0.0 and remove expressions 2024-02-16 11:03:48 +01:00
Andre601 e4c90730ba Add AdvancedBan Addon 2024-02-16 10:51:35 +01:00
Andre601 0694e08399 Remove unused import 2024-02-15 18:18:08 +01:00
Andre601 2fefa966dd Improve formatting page 2024-02-14 20:58:46 +01:00
Andre601 a57cf3df17 Update outdated info in get-started 2024-02-13 20:55:41 +00:00
Andre601 1ae7260cf3 Update Hangar status 2024-02-13 20:44:30 +00:00
Andre601 8459eee57d Fix wrong Modrinth ID. 2024-02-13 21:19:38 +01:00
Andre601 f5cebf42ab Change ~= to ~= 2024-02-13 21:05:52 +01:00
Andre601 731bdeba7d Change way of applying dependencies in ModrinthVersionUploader 2024-02-13 17:31:45 +01:00
Andre601 d14cda243a Bump mkdocs-material to 9.5.9 2024-02-10 14:23:29 +00:00
Andre601 0a98aad138 Use hook to remove backslash from | 2024-02-09 16:00:52 +01:00
Andre601 1249a08264 Make sure Anonymous is in double quotes. 2024-02-09 01:36:15 +00:00
Andre601 c0414a5962 Actually, starts with operator is better 2024-02-09 01:34:50 +00:00
Andre601 9cc4d4bb36 Add example for contains expression 2024-02-09 01:33:51 +00:00
Andre601 338358a2ab Why did it remove magiclink? 2024-02-09 01:23:41 +00:00
Andre601 805ba83bb6 fix typos and improve readme 2024-02-08 00:30:21 +00:00
Andre601 0f646a8f8d Merge pull request 'Update dependency mkdocs-material to v9.5.8' (#220) from renovate/mkdocs-material-9.x into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/220
2024-02-07 12:31:20 +00:00
Andre601's Renovate Bot 2519f09f6f Update dependency mkdocs-material to v9.5.8 2024-02-07 12:01:04 +00:00
Andre601 60bca9e3d7 Redo getting started guide and migration page 2024-02-07 01:41:08 +01:00
Andre601 da8f72fc50 Update mkdocs.yml... I think 2024-02-07 01:06:06 +01:00
Andre601 c42d4b8967 Add operand examples and open original in new tab 2024-02-05 12:46:30 +00:00
Andre601 0110854ae4 Fix some typo 2024-02-04 19:20:01 +00:00
Andre601 d6d5952cbc Merge pull request 'Update dependency org.junit.jupiter:junit-jupiter-engine to v5.10.2' (#219) from renovate/org.junit.jupiter-junit-jupiter-engine-5.x into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/219
2024-02-04 12:54:07 +00:00
Andre601's Renovate Bot f9d961cae7 Update dependency org.junit.jupiter:junit-jupiter-engine to v5.10.2 2024-02-04 12:01:05 +00:00
Andre601 a387feea23 Fix wrong operand for contains in docs 2024-02-03 17:39:51 +01:00
Andre601 359c58163b Only add ViaVersion on paper platform. 2024-02-03 17:34:55 +01:00
Andre601 0892f5cd1d Use <_ for contains instead of <- 2024-02-03 17:21:54 +01:00
Andre601 452a7e6dcf Merge pull request 'Update dependency mkdocs-material to v9.5.7' (#218) from renovate/mkdocs-material-9.x into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/218
2024-02-03 12:02:21 +00:00
Andre601's Renovate Bot 162dd10a73 Update dependency mkdocs-material to v9.5.7 2024-02-03 12:01:15 +00:00
Andre601 35120c5364 Bump version to 7.0.0 2024-01-31 21:42:24 +01:00
Andre601 6c828171d2 Add new operands to use 2024-01-31 21:41:22 +01:00
Andre601 dcc51f8458 [Docs] Improve profiles page 2024-01-30 17:32:10 +01:00
Andre601 1287ca62fe Fix platform for ${server host} 2024-01-29 23:24:18 +00:00
Andre601 1ff439fa95 Use Java's HttpClient for update checks 2024-01-29 16:37:49 +01:00
Andre601 ed0f832b2f Merge pull request 'Update dependency mkdocs-material to v9.5.6' (#217) from renovate/mkdocs-material-9.x into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/217
2024-01-28 12:02:57 +00:00
Andre601's Renovate Bot b8e6b2acc6 Update dependency mkdocs-material to v9.5.6 2024-01-28 12:01:18 +00:00
Andre601 fba32c91c5 Add page about expressions 2024-01-28 00:20:52 +01:00
Andre601 6505ded12b Use assertEquals(...) for testing 2024-01-27 23:25:17 +01:00
Andre601 53088affcb docs/profiles/index.md aktualisiert 2024-01-27 22:23:00 +00:00
Andre601 9c030764fa Merge pull request 'Update dependency mkdocs-glightbox to v0.3.7' (#215) from renovate/mkdocs-glightbox-0.x into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/215
2024-01-25 12:06:08 +00:00
Andre601 d5d58f1eea Merge branch 'master' into renovate/mkdocs-glightbox-0.x 2024-01-25 12:05:53 +00:00
Andre601 7181092821 Merge pull request 'Update dependency org.apache.httpcomponents.client5:httpclient5 to v5.3.1' (#216) from renovate/org.apache.httpcomponents.client5-httpclient5-5.x into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/216
2024-01-25 12:05:37 +00:00
Andre601's Renovate Bot fc5ee4f242 Update dependency org.apache.httpcomponents.client5:httpclient5 to v5.3.1 2024-01-25 12:01:04 +00:00
Andre601's Renovate Bot 6e74c4fb14 Update dependency mkdocs-glightbox to v0.3.7 2024-01-25 12:00:59 +00:00
Andre601 f6a27342e6 Merge pull request 'Update dependency mkdocs-material to v9.5.5' (#214) from renovate/mkdocs-material-9.x into master
Reviewed-on: https://codeberg.org/Andre601/AdvancedServerList/pulls/214
2024-01-24 12:17:57 +00:00
Andre601's Renovate Bot 38b50df2c9 Update dependency mkdocs-material to v9.5.5 2024-01-24 12:00:49 +00:00
72 changed files with 1659 additions and 646 deletions

No files matched your search

+2 -1
View File
@@ -20,7 +20,7 @@ steps:
- mv VersionUploader.jar ../../
- cd ../../
# Execute Jar file
- java -jar VersionUploader.jar --modrinth
- java -jar VersionUploader.jar --all
uploadToCodeberg:
image: woodpeckerci/plugin-gitea-release
settings:
@@ -29,5 +29,6 @@ steps:
- "bungeecord/target/AdvancedServerList-*.jar"
- "paper/target/AdvancedServerList-*.jar"
- "velocity/target/AdvancedServerList-*.jar"
- "addons/banplugins/target/AdvancedServerList-*.jar"
api_key:
from_secret: CB_TOKEN
+8 -4
View File
@@ -1,6 +1,6 @@
# 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.
AdvancedServerList is a plugin that allows 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.
## Downloads
@@ -30,29 +30,33 @@ AdvancedServerList is a plugin that allowys you to customize your server's MOTD,
</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.
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 it automatically 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 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, or while PAPIProxyBridge is installed on your proxy, can you also use placeholders from [PlaceholderAPI][placeholderapi] using its `%<identifier>_<values>%` placeholder format. PlaceholderAPI needs to be installed for that.
When using the SpigotMC/PaperMC version, or while [PAPIProxyBridge][papiproxybridge] is installed on your proxy, can you also use placeholders from [PlaceholderAPI][placeholderapi] using its `%<identifier>_<values>%` placeholder format. PlaceholderAPI needs to be installed on the server (not proxy) 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.
+113
View File
@@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ MIT License
~
~ Copyright (c) 2022-2024 Andre_601
~
~ Permission is hereby granted, free of charge, to any person obtaining a copy
~ of this software and associated documentation files (the "Software"), to deal
~ in the Software without restriction, including without limitation the rights
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
~ copies of the Software, and to permit persons to whom the Software is
~ furnished to do so, subject to the following conditions:
~
~ The above copyright notice and this permission notice shall be included in all
~ copies or substantial portions of the Software.
~
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
~ SOFTWARE.
~
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>parent</artifactId>
<groupId>ch.andre601.advancedserverlist</groupId>
<version>parent</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>banplugins</artifactId>
<packaging>jar</packaging>
<version>${plugin.version}</version>
<properties>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>sonatype</id>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</repository>
<repository>
<id>papermc</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<!-- Proxy/Server -->
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.20-R0.2</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.20-R0.2</version>
<type>javadoc</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.20-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- Plugin APIs -->
<dependency>
<groupId>com.github.DevLeoko</groupId>
<artifactId>AdvancedBan</artifactId>
<version>v2.3.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>ch.andre601.asl-api</groupId>
<artifactId>api</artifactId>
<version>${api.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>AdvancedServerList-BanPlugins-Addon-${plugin.version}</finalName>
<resources>
<resource>
<filtering>true</filtering>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.12.1</version>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,55 @@
/*
* MIT License
*
* Copyright (c) 2022-2024 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.banplugins.bungeecord;
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
import ch.andre601.advancedserverlist.banplugins.placeholders.AdvancedBanPlaceholders;
import java.util.HashMap;
import java.util.Map;
public enum BungeeCordBanPlugins{
ADVANCED_BAN("AdvancedBan", new AdvancedBanPlaceholders());
private final String plugin;
private final PlaceholderProvider placeholderProvider;
BungeeCordBanPlugins(String plugin, PlaceholderProvider placeholderProvider){
this.plugin = plugin;
this.placeholderProvider = placeholderProvider;
}
public static Map<String, PlaceholderProvider> getBanPlugins(){
Map<String, PlaceholderProvider> content = new HashMap<>();
for(BungeeCordBanPlugins bungeeCordBanPlugins : BungeeCordBanPlugins.values()){
content.put(bungeeCordBanPlugins.plugin, bungeeCordBanPlugins.placeholderProvider);
}
return content;
}
}
@@ -0,0 +1,68 @@
/*
* MIT License
*
* Copyright (c) 2022-2024 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.banplugins.bungeecord;
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
import ch.andre601.advancedserverlist.api.AdvancedServerListAPI;
import net.md_5.bungee.api.plugin.Plugin;
import java.util.Map;
public class BungeeCordBanPluginsAddon extends Plugin{
@Override
public void onEnable(){
if(getProxy().getPluginManager().getPlugin("AdvancedServerList") == null){
getLogger().warning("AdvancedServerList is not enabled. This plugin requires it to function!");
return;
}
int loadedPlaceholders = loadPlaceholderProviders();
if(loadedPlaceholders == 0){
getLogger().warning("No compatible Ban plugin was found to register placeholders for.");
}else{
getLogger().info("Loaded " + loadedPlaceholders + " Placeholder Set(s) for AdvancedServerList!");
}
}
private int loadPlaceholderProviders(){
int loaded = 0;
Map<String, PlaceholderProvider> banPlugins = BungeeCordBanPlugins.getBanPlugins();
AdvancedServerListAPI api = AdvancedServerListAPI.get();
for(Map.Entry<String, PlaceholderProvider> entry : banPlugins.entrySet()){
if(getProxy().getPluginManager().getPlugin(entry.getKey()) != null){
getLogger().info("Registering Placeholders for " + entry.getKey() + "...");
api.addPlaceholderProvider(entry.getValue());
getLogger().info("Placeholder successfully registered!");
loaded++;
}
}
return loaded;
}
}
@@ -0,0 +1,55 @@
/*
* MIT License
*
* Copyright (c) 2022-2024 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.banplugins.paper;
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
import ch.andre601.advancedserverlist.banplugins.placeholders.AdvancedBanPlaceholders;
import java.util.HashMap;
import java.util.Map;
public enum PaperBanPlugins{
ADVANCED_BAN("AdvancedBan", new AdvancedBanPlaceholders());
private final String plugin;
private final PlaceholderProvider placeholderProvider;
PaperBanPlugins(String plugin, PlaceholderProvider placeholderProvider){
this.plugin = plugin;
this.placeholderProvider = placeholderProvider;
}
public static Map<String, PlaceholderProvider> getBanPlugins(){
Map<String, PlaceholderProvider> content = new HashMap<>();
for(PaperBanPlugins paperBanPlugins : PaperBanPlugins.values()){
content.put(paperBanPlugins.plugin, paperBanPlugins.placeholderProvider);
}
return content;
}
}
@@ -0,0 +1,71 @@
/*
* MIT License
*
* Copyright (c) 2022-2024 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.banplugins.paper;
import ch.andre601.advancedserverlist.api.AdvancedServerListAPI;
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.Map;
public class PaperBanPluginsAddon extends JavaPlugin{
@Override
public void onEnable(){
if(!getServer().getPluginManager().isPluginEnabled("AdvancedServerList")){
getLogger().warning("AdvancedServerList is not enabled. This plugin requires it to function!");
getServer().getPluginManager().disablePlugin(this);
return;
}
int loadedPlaceholders = loadPlaceholderProviders();
if(loadedPlaceholders == 0){
getLogger().warning("No compatible Ban plugin was found to register placeholders for. Disabling plugin...");
getServer().getPluginManager().disablePlugin(this);
}else{
getLogger().info("Loaded " + loadedPlaceholders + " Placeholder Set(s) for AdvancedServerList!");
}
}
private int loadPlaceholderProviders(){
int loaded = 0;
Map<String, PlaceholderProvider> banPlugins = PaperBanPlugins.getBanPlugins();
AdvancedServerListAPI api = AdvancedServerListAPI.get();
for(Map.Entry<String, PlaceholderProvider> entry : banPlugins.entrySet()){
if(getServer().getPluginManager().isPluginEnabled(entry.getKey())){
getLogger().info("Registering Placeholders for " + entry.getKey() + "...");
api.addPlaceholderProvider(entry.getValue());
getLogger().info("Placeholder successfully registered!");
loaded++;
}
}
return loaded;
}
}
@@ -0,0 +1,96 @@
/*
* MIT License
*
* Copyright (c) 2022-2024 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.banplugins.placeholders;
import ch.andre601.advancedserverlist.banplugins.providers.AdvancedBanProvider;
import ch.andre601.advancedserverlist.api.PlaceholderProvider;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import java.util.Arrays;
public class AdvancedBanPlaceholders extends PlaceholderProvider{
private final AdvancedBanProvider provider = new AdvancedBanProvider();
public AdvancedBanPlaceholders(){
super("advancedban");
}
@Override
public String parsePlaceholder(String placeholder, GenericPlayer player, GenericServer server){
String[] args = placeholder.split("\\s");
return switch(placeholder) {
// Mute-related placeholders
case "isMuted" -> String.valueOf(provider.muted(player));
case "muteReason" -> provider.muteReason(player);
case "muteDuration" -> {
if(args.length == 1)
yield provider.muteDuration(player);
if(args.length > 2)
yield null;
boolean fromStart = Boolean.getBoolean(args[1]);
yield provider.muteDuration(player, fromStart);
}
case "muteExpiration" -> {
if(args.length == 1)
yield provider.muteExpirationDate(player);
String pattern = String.join(" ", Arrays.copyOfRange(args, 1, args.length));
yield provider.banExpirationDate(player, pattern);
}
// Ban-related placeholders
case "isBanned" -> String.valueOf(provider.banned(player));
case "banReason" -> provider.banReason(player);
case "banDuration" -> {
if(args.length == 1)
yield provider.banDuration(player);
if(args.length > 2)
yield null;
boolean fromStart = Boolean.getBoolean(args[1]);
yield provider.banDuration(player, fromStart);
}
case "banExpiration" -> {
if(args.length == 1)
yield provider.banExpirationDate(player);
String pattern = String.join(" ", Arrays.copyOfRange(args, 1, args.length));
yield provider.banExpirationDate(player, pattern);
}
// Unknown/Invalid placeholder.
default -> null;
};
}
}
@@ -0,0 +1,141 @@
/*
* MIT License
*
* Copyright (c) 2022-2024 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.banplugins.providers;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import com.google.common.base.Strings;
import com.google.common.primitives.UnsignedLongs;
import me.leoko.advancedban.manager.PunishmentManager;
import me.leoko.advancedban.utils.Punishment;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
public class AdvancedBanProvider implements PunishmentProvider{
private static PunishmentManager getAPI(){
return PunishmentManager.get();
}
private static String noDashesUUID(UUID uuid){
return hexString(uuid.getMostSignificantBits()) + hexString(uuid.getLeastSignificantBits());
}
private static Punishment mute(GenericPlayer player){
return getAPI().getMute(noDashesUUID(player.getUUID()));
}
private static Punishment ban(GenericPlayer player){
return getAPI().getBan(noDashesUUID(player.getUUID()));
}
private static String hexString(long unsigned){
return Strings.padStart(UnsignedLongs.toString(unsigned, 16), 16, '0');
}
@Override
public boolean muted(GenericPlayer player){
return getAPI().isMuted(noDashesUUID(player.getUUID()));
}
@Override
public boolean banned(GenericPlayer player){
return getAPI().isBanned(noDashesUUID(player.getUUID()));
}
@Override
public String muteReason(GenericPlayer player){
Punishment mute = mute(player);
if(mute == null)
return null;
return mute.getReason();
}
@Override
public String banReason(GenericPlayer player){
Punishment ban = ban(player);
if(ban == null)
return null;
return ban.getReason();
}
@Override
public String muteDuration(GenericPlayer player){
return muteDuration(player, false);
}
@Override
public String banDuration(GenericPlayer player){
return banDuration(player, false);
}
@Override
public String muteExpirationDate(GenericPlayer player, String pattern){
Punishment mute = mute(player);
if(mute == null)
return null;
long end = mute.getEnd();
if(end < 0L)
return "never";
Date date = new Date(end);
return new SimpleDateFormat(pattern).format(date);
}
@Override
public String banExpirationDate(GenericPlayer player, String pattern){
Punishment ban = ban(player);
if(ban == null)
return null;
long end = ban.getEnd();
if(end < 0L)
return "never";
Date date = new Date(end);
return new SimpleDateFormat(pattern).format(date);
}
public String muteDuration(GenericPlayer player, boolean fromStart){
Punishment mute = mute(player);
if(mute == null)
return null;
return mute.getDuration(fromStart);
}
public String banDuration(GenericPlayer player, boolean fromStart){
Punishment ban = ban(player);
if(ban == null)
return null;
return ban.getDuration(fromStart);
}
}
@@ -0,0 +1,58 @@
/*
* MIT License
*
* Copyright (c) 2022-2024 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.banplugins.providers;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import java.time.format.DateTimeFormatter;
public interface PunishmentProvider{
boolean muted(GenericPlayer player);
boolean banned(GenericPlayer player);
String muteReason(GenericPlayer player);
String banReason(GenericPlayer player);
String muteDuration(GenericPlayer player);
String banDuration(GenericPlayer player);
default String muteExpirationDate(GenericPlayer player){
return muteExpirationDate(player, "dd, MMM yyyy HH:mm:ss");
}
String muteExpirationDate(GenericPlayer player, String pattern);
default String banExpirationDate(GenericPlayer player){
return banExpirationDate(player, "dd, MMM yyyy HH:mm:ss");
}
String banExpirationDate(GenericPlayer player, String pattern);
}
@@ -0,0 +1,10 @@
name: 'ASLBanPluginsAddon'
author: 'Andre_601'
version: '${plugin.version}'
description: 'AdvancedBan Add-on for AdvancedServerList to add placeholders to use.'
softDepends:
- AdvancedServerList
- AdvancedBan
main: 'ch.andre601.advancedserverlist.banplugins.bungeecord.BungeeCordBanPluginsAddon'
@@ -0,0 +1,16 @@
name: 'ASLBanPluginsAddon'
author: 'Andre_601'
version: '${plugin.version}'
main: 'ch.andre601.advancedserverlist.banplugins.paper.AdvancedBanPlaceholders'
api-version: '1.19'
dependencies:
server:
AdvancedServerList:
required: false
load: BEFORE
AdvancedBans:
required: false
load: BEFORE
-10
View File
@@ -108,12 +108,6 @@
<version>4.1.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
@@ -149,10 +143,6 @@
<pattern>org.bstats</pattern>
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.bstats</shadedPattern>
</relocation>
<relocation>
<pattern>okhttp3</pattern>
<shadedPattern>ch.andre601.advancedserverlist.bungeecord.depends.okhttp3</shadedPattern>
</relocation>
</relocations>
<filters>
<filter>
@@ -85,6 +85,8 @@ public class BungeeServerPlaceholders extends PlaceholderProvider{
if(args.length == 2){
ServerInfo info = proxy.getServers().get(args[1]);
if(info == null)
yield null;
yield String.valueOf(((InetSocketAddress)info.getSocketAddress()).getHostString());
}
+1 -7
View File
@@ -98,12 +98,6 @@
<version>2.10.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.unascribed</groupId>
<artifactId>flexver-java</artifactId>
@@ -135,7 +129,7 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.1</version>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -30,18 +30,17 @@ 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.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@@ -49,12 +48,10 @@ 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;
@SuppressWarnings("FieldCanBeLocal")
// 'https://api.modrinth.com/v2/project/advancedserverlist/version?loaders=["%s"]' URL encoded.
private final String url = "https://api.modrinth.com/v2/project/advancedserverlist/version?loaders=%%5B%%22%s%%22%%5D";
private final HttpClient client = HttpClient.newHttpClient();
private final Type listType = new TypeToken<ArrayList<ModrinthVersion>>(){}.getType();
private final Gson gson = new GsonBuilder()
@@ -65,6 +62,10 @@ public class UpdateChecker{
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(new UpdateCheckThread());
private final AdvancedServerList<?> core;
private final PluginLogger logger;
private final String loader;
public UpdateChecker(AdvancedServerList<?> core){
this.core = core;
this.logger = core.getPlugin().getPluginLogger();
@@ -73,12 +74,27 @@ public class UpdateChecker{
startUpdateChecker();
}
public void startUpdateChecker(){
public void disable(){
executor.shutdown();
try{
if(!executor.awaitTermination(1, TimeUnit.SECONDS)){
executor.shutdownNow();
if(!executor.awaitTermination(1, TimeUnit.SECONDS))
logger.warn("Scheduler did not terminate in time!");
}
}catch(InterruptedException ex){
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
private void startUpdateChecker(){
executor.scheduleAtFixedRate(() -> {
logger.info("Checking for a new update...");
checkUpdate().whenComplete((version, throwable) -> {
logger.info("Looking for an update...");
performUpdateCheck().whenComplete((version, throwable) -> {
if(version == null || throwable != null){
logger.warn("Failed to look for any updates. See previous messages for reasons.");
logger.warn("Failed to look for any updates. Check previous messages for causes.");
return;
}
@@ -87,106 +103,44 @@ public class UpdateChecker{
int result = version.compare(core.getVersion());
switch(result){
case -1 -> printUpdateBanner(version);
case 0 -> logger.info("No new update found. You're running the latest version!");
case 0 -> logger.info("You are 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()
"Your version (%s) seems to be newer than the latest release (%s). Are you running a dev build?",
core.getVersion(), version.versionNumber()
);
}
});
}, 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> performUpdateCheck(){
String finalUrl = String.format(url, loader);
logger.debug(UpdateChecker.class, "Checking '%s' for updates...", finalUrl);
if(core.getVersion().equals("UNKNOWN")){
logger.warn("Cannot perform update check. Plugin version is 'UNKNOWN'!");
return CompletableFuture.completedFuture(null);
}
}
private CompletableFuture<ModrinthVersion> checkUpdate(){
return CompletableFuture.supplyAsync(() -> {
PluginLogger logger = core.getPlugin().getPluginLogger();
String finalUrl = String.format(url, loader);
logger.debug(UpdateChecker.class, "Looking for updates on '%s'...", finalUrl);
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(finalUrl)
HttpRequest request;
try{
request = HttpRequest.newBuilder()
.uri(new URI(finalUrl))
.header("User-Agent", "AdvancedServerList-" + loader + "/" + core.getVersion())
.build();
logger.debug(UpdateChecker.class, "Connecting to website...");
try(Response response = client.newCall(request).execute()){
logger.debug(UpdateChecker.class, "Connected! Checking response code...");
if(!response.isSuccessful()){
logger.warn("Encountered a non-successful response from Modrinth! Code: %d", response.code());
return null;
}
logger.debug(UpdateChecker.class, "Received positive response code '%d'", response.code());
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;
}
logger.debug(UpdateChecker.class, "Received non-empty response body. Parsing it from JSON Array...");
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);
logger.debug(UpdateChecker.class, "Found entry '%s'", version);
if(version.getVersionNumber() == null || version.getVersionNumber().isEmpty()){
logger.warn("Cannot check latest version. Received version number was null/empty.");
return null;
}
logger.debug(UpdateChecker.class, "Successfully parsed version!");
return version;
}catch(JsonSyntaxException ex){
logger.warn("Encountered invalid JSON from Response body!", ex);
}catch(URISyntaxException ex){
logger.warn("Cannot perform update check. URL '%s' is not a valid URI.", ex, finalUrl);
return CompletableFuture.completedFuture(null);
}
return client.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8))
.thenApply(response -> new ArrayList<ModrinthVersion>(gson.fromJson(response.body(), listType)))
.thenApply(list -> list.get(0))
.exceptionally((throwable) -> {
logger.warn("Encountered exception while performing update check!", throwable);
return null;
}catch(IOException ex){
logger.warn("Encountered Exception while checking for an update!", ex);
return null;
}
});
});
}
private void printUpdateBanner(ModrinthVersion version){
@@ -194,53 +148,33 @@ public class UpdateChecker{
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.getVersionNumber());
logger.info("Modrinth: %s", version.versionNumber());
logger.info("");
if(!version.isRelease()){
logger.info("WARNING: This release is an Alpha/Beta! It may contain");
logger.info(" breaking changes!");
logger.info("WARNING!");
logger.info("This is a %s version and may contain breaking changes and/or bugs!", version.versionType());
logger.info("");
}
logger.info("You can download the latest release from here:");
logger.info("https://modrinth.com/plugin/advancedserverlist/version/%s", version.getId());
logger.info("Download the latest version from here:");
logger.info("https://modrinth.com/plugin/advancedserverlist/version/%s", version.id());
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 record ModrinthVersion(String id, String versionNumber, String versionType){
public boolean isRelease(){
return versionType.equals("release");
return versionType().equals("release");
}
public int compare(String version){
return FlexVerComparator.compare(version, versionNumber);
return FlexVerComparator.compare(version, versionNumber());
}
@Override
public String toString(){
return String.format("ModrinthVersion{id=%s,versionNumber=%s,versionType=%s}", id, versionNumber, versionNumber);
return String.format("ModrinthVersion{id=%s,versionNumber=%s,versionType=%s}", id, versionNumber, versionType);
}
}
}
@@ -165,7 +165,7 @@ public class FileHandler{
return false;
}
profiles.sort(Comparator.comparing(ServerListProfile::getPriority).reversed());
profiles.sort(Comparator.comparing(ServerListProfile::priority).reversed());
return !profiles.isEmpty();
}
@@ -29,63 +29,34 @@ import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.api.profiles.ProfileEntry;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.profiles.conditions.Expression;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionEngine;
import ch.andre601.advancedserverlist.core.profiles.conditions.templates.ExpressionErrorTemplate;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionsWarnHelper;
import ch.andre601.advancedserverlist.core.profiles.conditions.templates.ExpressionTemplate;
import ch.andre601.advancedserverlist.core.profiles.replacer.StringReplacer;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.serialize.SerializationException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.StringJoiner;
public class ServerListProfile{
private final int priority;
private final String condition;
private final List<Expression> expressions;
private final ProfileEntry defaultProfile;
private final List<ProfileEntry> profiles;
private final Random random = new Random();
public ServerListProfile(int priority, String condition, List<Expression> expressions, ProfileEntry defaultProfile, List<ProfileEntry> profiles){
this.priority = priority;
this.condition = condition;
this.expressions = expressions;
this.defaultProfile = defaultProfile;
this.profiles = profiles;
}
public int getPriority(){
return priority;
}
public record ServerListProfile(int priority, String condition, ProfileEntry defaultProfile, List<ProfileEntry> profiles){
private static final Random random = new Random();
public boolean evalConditions(ExpressionEngine expressionEngine, PluginLogger logger, GenericPlayer player, GenericServer server){
if(condition != null && !condition.isEmpty()){
ExpressionTemplate template = expressionEngine.compile(condition, logger, player, server);
if(template instanceof ExpressionErrorTemplate errorTemplate){
logger.warn(errorTemplate.instantiateWithStringResult().evaluate());
return true;
}
return template.instantiateWithBooleanResult().evaluate();
}
if(expressions.isEmpty())
if(condition == null || condition.isEmpty())
return true;
for(Expression expression : expressions){
if(!expression.evaluate(player, server))
ExpressionsWarnHelper warnHelper = new ExpressionsWarnHelper(condition);
ExpressionTemplate template = expressionEngine.compile(condition, player, server, warnHelper);
if(warnHelper.hasWarnings()){
warnHelper.printWarnings(logger);
if(template == null)
return false;
}
return true;
return template.instantiateWithBooleanResult().evaluate();
}
public ProfileEntry getRandomProfile(){
@@ -101,10 +72,6 @@ public class ServerListProfile{
}
}
public ProfileEntry getDefaultProfile(){
return defaultProfile;
}
/*
* Returns true if the Profile...
* ...doesn't have any valid MOTD set AND
@@ -113,12 +80,12 @@ public class ServerListProfile{
* ...doesn't have a favicon set.
*/
public boolean isInvalidProfile(){
if(profiles.isEmpty())
return defaultProfile.isInvalid();
if(profiles().isEmpty())
return defaultProfile().isInvalid();
boolean profilesValid = false;
for(ProfileEntry profile : profiles){
for(ProfileEntry profile : profiles()){
if(profile.isInvalid())
continue;
@@ -134,7 +101,6 @@ public class ServerListProfile{
private final String fileName;
private final ConfigurationNode node;
private final int priority;
private final List<Expression> expressions = new ArrayList<>();
private final PluginLogger logger;
@@ -152,7 +118,6 @@ public class ServerListProfile{
public static Builder resolve(String fileName, ConfigurationNode node, PluginLogger logger){
return new Builder(fileName, node, logger)
.resolveCondition()
.resolveExpressions()
.resolveProfiles()
.resolveDefaultProfile();
}
@@ -166,43 +131,6 @@ public class ServerListProfile{
return this;
}
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;
StringJoiner joiner = new StringJoiner(" and ");
for(String str : temp){
Expression expr = Expression.resolve(str);
if(expr.getResult() != Expression.ExpressionResult.VALID){
logger.warn("Found invalid expression in condition '%s'! Reason: %s", str, expr.getResult().getMessage());
continue;
}
joiner.add(str);
this.expressions.add(expr);
}
if(!this.expressions.isEmpty()){
logger.warn("'%s' uses 'conditions' which is deprecated in favour of the newer 'condition' option.", fileName);
logger.warn("The 'conditions' option will be removed in a future version.");
logger.warn("");
logger.warn("Please migrate your condition(s) over from the old option to the new option like this:");
logger.warn("consition: '%s'", joiner.toString());
}
return this;
}
private Builder resolveProfiles(){
try{
this.profiles = node.node("profiles").getList(ProfileEntry.class);
@@ -223,7 +151,7 @@ public class ServerListProfile{
}
public ServerListProfile build(){
return new ServerListProfile(this.priority, this.condition, this.expressions, this.defaultProfile, this.profiles);
return new ServerListProfile(this.priority, this.condition, this.defaultProfile, this.profiles);
}
}
}
@@ -28,6 +28,7 @@ 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.interfaces.PluginLogger;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionsWarnHelper;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.Token;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.readers.TokenReader;
import com.google.common.collect.Ordering;
@@ -47,7 +48,7 @@ public class ExpressionTokenizer{
this.tokenReaders = TOKEN_READER_ORDERING.immutableSortedCopy(tokenReaders);
}
public List<Token> parse(String text, PluginLogger logger, GenericPlayer player, GenericServer server){
public List<Token> parse(String text, GenericPlayer player, GenericServer server, ExpressionsWarnHelper warnHelper){
ParsePosition position = new ParsePosition(0);
List<Token> tokens = new LinkedList<>();
@@ -63,13 +64,13 @@ public class ExpressionTokenizer{
for(TokenReader tokenReader : tokenReaders){
Token token;
if(null != (token = tokenReader.read(text, position, player, server))){
if(null != (token = tokenReader.read(text, position, player, server, warnHelper))){
tokens.add(token);
continue next_token;
}
}
logger.warn("Illegal token '%c' at index %d.", text.charAt(position.getIndex()), position.getIndex());
warnHelper.appendWarning(position.getIndex(), "Illegal token '%c'.");
break;
}
@@ -62,8 +62,8 @@ public class ExpressionEngine{
this.templateParser = new ExpressionTemplateParser(options.operators, options.valueReaders);
}
public ExpressionTemplate compile(String expression, PluginLogger logger, GenericPlayer player, GenericServer server){
return templateParser.parse(tokenizer.parse(expression, logger, player, server));
public ExpressionTemplate compile(String expression, GenericPlayer player, GenericServer server, ExpressionsWarnHelper warnHelper){
return templateParser.parse(tokenizer.parse(expression, player, server, warnHelper), warnHelper);
}
public static class Options{
@@ -94,10 +94,14 @@ public class ExpressionEngine{
.tokenReader(new PatternTokenReader(DefaultTokens.OR, "or"))
.tokenReader(new PatternTokenReader(DefaultTokens.OR, "||"))
.tokenReader(new PatternTokenReader(DefaultTokens.EQUAL, "=="))
.tokenReader(new PatternTokenReader(DefaultTokens.EQUAL_IGNORE_CASE, "=~"))
.tokenReader(new PatternTokenReader(DefaultTokens.EQUAL, "="))
.tokenReader(new PatternTokenReader(DefaultTokens.EQUAL_IGNORE_CASE, "~"))
.tokenReader(new PatternTokenReader(DefaultTokens.NOT_EQUAL, "!="))
.tokenReader(new PatternTokenReader(DefaultTokens.EQUAL_IGNORE_CASE, "~="))
.tokenReader(new PatternTokenReader(DefaultTokens.NOT_EQUAL_IGNORE_CASE, "!~"))
.tokenReader(new PatternTokenReader(DefaultTokens.STARTS_WITH, "|-"))
.tokenReader(new PatternTokenReader(DefaultTokens.ENDS_WITH, "-|"))
.tokenReader(new PatternTokenReader(DefaultTokens.CONTAINS, "<_"))
.tokenReader(new PatternTokenReader(DefaultTokens.GREATER_OR_EQUAL_THAN, ">="))
.tokenReader(new PatternTokenReader(DefaultTokens.LESSER_OR_EQUAL_THAN, "<="))
.tokenReader(new PatternTokenReader(DefaultTokens.OPENING_PARENTHESIS, "("))
@@ -125,6 +129,9 @@ public class ExpressionEngine{
.operator(DefaultTokens.NOT_EQUAL, Operator.of(25, ExpressionTemplates::notEqual))
.operator(DefaultTokens.EQUAL_IGNORE_CASE, Operator.of(25, ExpressionTemplates::equalIgnoreCase))
.operator(DefaultTokens.NOT_EQUAL_IGNORE_CASE, Operator.of(25, ExpressionTemplates::notEqualIgnoreCase))
.operator(DefaultTokens.STARTS_WITH, Operator.of(25, ExpressionTemplates::startsWith))
.operator(DefaultTokens.ENDS_WITH, Operator.of(25, ExpressionTemplates::endsWith))
.operator(DefaultTokens.CONTAINS, Operator.of(25, ExpressionTemplates::contains))
.operator(DefaultTokens.GREATER_THAN, Operator.of(25, ExpressionTemplates::greater))
.operator(DefaultTokens.GREATER_OR_EQUAL_THAN, Operator.of(25, ExpressionTemplates::greaterOrEqual))
.operator(DefaultTokens.LESSER_THAN, Operator.of(25, ExpressionTemplates::less))
@@ -32,7 +32,7 @@ import java.util.Collection;
public class Expressions{
public static ToBooleanExpression negate(ToBooleanExpression expression){
return new AbstractUnaryToBooleanExpression<>(expression) {
return new AbstractUnaryToBooleanExpression<>(expression){
@Override
public boolean evaluate(){
return !delegate.evaluate();
@@ -41,7 +41,7 @@ public class Expressions{
}
public static ToBooleanExpression and(Collection<ToBooleanExpression> operands){
return new AbstractToBooleanExpression<>(operands) {
return new AbstractToBooleanExpression<>(operands){
@Override
public boolean evaluate(){
for(ToBooleanExpression operand : operands){
@@ -55,7 +55,7 @@ public class Expressions{
}
public static ToBooleanExpression or(Collection<ToBooleanExpression> operands){
return new AbstractToBooleanExpression<>(operands) {
return new AbstractToBooleanExpression<>(operands){
@Override
public boolean evaluate(){
for(ToBooleanExpression operand : operands){
@@ -69,7 +69,7 @@ public class Expressions{
}
public static ToStringExpression concat(Collection<ToStringExpression> operands){
return new AbstractToStringExpression<>(operands) {
return new AbstractToStringExpression<>(operands){
@Override
public String evaluate(){
StringBuilder result = new StringBuilder();
@@ -83,7 +83,7 @@ public class Expressions{
}
public static ToBooleanExpression equal(ToStringExpression a, ToStringExpression b){
return new AbstractBinaryToBooleanExpression<>(a, b) {
return new AbstractBinaryToBooleanExpression<>(a, b){
@Override
public boolean evaluate(){
return a.evaluate().equals(b.evaluate());
@@ -92,7 +92,7 @@ public class Expressions{
}
public static ToBooleanExpression notEqual(ToStringExpression a, ToStringExpression b){
return new AbstractBinaryToBooleanExpression<>(a, b) {
return new AbstractBinaryToBooleanExpression<>(a, b){
@Override
public boolean evaluate(){
return !a.evaluate().equals(b.evaluate());
@@ -101,7 +101,7 @@ public class Expressions{
}
public static ToBooleanExpression equalIgnoreCase(ToStringExpression a, ToStringExpression b){
return new AbstractBinaryToBooleanExpression<>(a, b) {
return new AbstractBinaryToBooleanExpression<>(a, b){
@Override
public boolean evaluate(){
return a.evaluate().equalsIgnoreCase(b.evaluate());
@@ -110,7 +110,7 @@ public class Expressions{
}
public static ToBooleanExpression notEqualIgnoreCase(ToStringExpression a, ToStringExpression b){
return new AbstractBinaryToBooleanExpression<>(a, b) {
return new AbstractBinaryToBooleanExpression<>(a, b){
@Override
public boolean evaluate(){
return !a.evaluate().equalsIgnoreCase(b.evaluate());
@@ -118,8 +118,35 @@ public class Expressions{
};
}
public static ToBooleanExpression startsWith(ToStringExpression a, ToStringExpression b){
return new AbstractBinaryToBooleanExpression<>(a, b){
@Override
public boolean evaluate(){
return a.evaluate().startsWith(b.evaluate());
}
};
}
public static ToBooleanExpression endsWith(ToStringExpression a, ToStringExpression b){
return new AbstractBinaryToBooleanExpression<>(a, b){
@Override
public boolean evaluate(){
return a.evaluate().endsWith(b.evaluate());
}
};
}
public static ToBooleanExpression contains(ToStringExpression a, ToStringExpression b){
return new AbstractBinaryToBooleanExpression<>(a, b){
@Override
public boolean evaluate(){
return a.evaluate().contains(b.evaluate());
}
};
}
public static ToBooleanExpression greaterThan(ToDoubleExpression a, ToDoubleExpression b){
return new AbstractBinaryToBooleanExpression<>(a, b) {
return new AbstractBinaryToBooleanExpression<>(a, b){
@Override
public boolean evaluate(){
return a.evaluate() > b.evaluate();
@@ -128,7 +155,7 @@ public class Expressions{
}
public static ToBooleanExpression greaterOrEqualThan(ToDoubleExpression a, ToDoubleExpression b){
return new AbstractBinaryToBooleanExpression<>(a, b) {
return new AbstractBinaryToBooleanExpression<>(a, b){
@Override
public boolean evaluate(){
return a.evaluate() >= b.evaluate();
@@ -137,7 +164,7 @@ public class Expressions{
}
public static ToBooleanExpression lessThan(ToDoubleExpression a, ToDoubleExpression b){
return new AbstractBinaryToBooleanExpression<>(a, b) {
return new AbstractBinaryToBooleanExpression<>(a, b){
@Override
public boolean evaluate(){
return a.evaluate() < b.evaluate();
@@ -146,7 +173,7 @@ public class Expressions{
}
public static ToBooleanExpression lessOrEqualThan(ToDoubleExpression a, ToDoubleExpression b){
return new AbstractBinaryToBooleanExpression<>(a, b) {
return new AbstractBinaryToBooleanExpression<>(a, b){
@Override
public boolean evaluate(){
return a.evaluate() <= b.evaluate();
@@ -155,7 +182,7 @@ public class Expressions{
}
public static ToDoubleExpression sum(Collection<ToDoubleExpression> operands){
return new AbstractToDoubleExpression<>(operands) {
return new AbstractToDoubleExpression<>(operands){
@Override
public double evaluate(){
double result = 0;
@@ -169,7 +196,7 @@ public class Expressions{
}
public static ToDoubleExpression product(Collection<ToDoubleExpression> operands){
return new AbstractToDoubleExpression<>(operands) {
return new AbstractToDoubleExpression<>(operands){
@Override
public double evaluate(){
double result = 1;
@@ -183,7 +210,7 @@ public class Expressions{
}
public static ToDoubleExpression sub(ToDoubleExpression a, ToDoubleExpression b){
return new AbstractBinaryToDoubleExpression<>(a, b) {
return new AbstractBinaryToDoubleExpression<>(a, b){
@Override
public double evaluate(){
return a.evaluate() - b.evaluate();
@@ -192,7 +219,7 @@ public class Expressions{
}
public static ToDoubleExpression div(ToDoubleExpression a, ToDoubleExpression b){
return new AbstractBinaryToDoubleExpression<>(a, b) {
return new AbstractBinaryToDoubleExpression<>(a, b){
@Override
public double evaluate(){
return a.evaluate() / b.evaluate();
@@ -201,7 +228,7 @@ public class Expressions{
}
public static ToDoubleExpression negateNumber(ToDoubleExpression a){
return new AbstractUnaryToDoubleExpression<>(a) {
return new AbstractUnaryToDoubleExpression<>(a){
@Override
public double evaluate(){
return -delegate.evaluate();
@@ -23,36 +23,40 @@
*
*/
package ch.andre601.advancedserverlist.core.profiles.conditions.templates;
package ch.andre601.advancedserverlist.core.profiles.conditions.expressions;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ToBooleanExpression;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ToDoubleExpression;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ToStringExpression;
import ch.andre601.advancedserverlist.core.interfaces.PluginLogger;
public class ExpressionErrorTemplate implements ExpressionTemplate{
import java.util.ArrayList;
import java.util.List;
public class ExpressionsWarnHelper{
private final String msg;
private final String expression;
private final List<Context> warnings = new ArrayList<>();
private ExpressionErrorTemplate(String msg){
this.msg = msg;
public ExpressionsWarnHelper(String expression){
this.expression = expression;
}
public static ExpressionErrorTemplate of(String msg){
return new ExpressionErrorTemplate(msg);
public void appendWarning(int position, String warning, Object... args){
warnings.add(new Context(position, String.format(warning, args)));
}
@Override
public ToStringExpression instantiateWithStringResult(){
return ToStringExpression.literal(msg);
public boolean hasWarnings(){
return !warnings.isEmpty();
}
@Override
public ToDoubleExpression instantiateWithDoubleResult(){
return ToDoubleExpression.literal(0);
public void printWarnings(PluginLogger logger){
logger.warn("Encountered %d issue(s) while parsing expression '%s'", warnings.size(), expression);
for(Context warning : warnings){
if(warning.position() > -1){
logger.warn(" - At Position %d: %s", warning.position(), warning.message());
}else{
logger.warn(" - Cause: %s", warning.message());
}
}
}
@Override
public ToBooleanExpression instantiateWithBooleanResult(){
return ToBooleanExpression.literal(false);
}
public record Context(int position, String message){}
}
@@ -25,6 +25,7 @@
package ch.andre601.advancedserverlist.core.profiles.conditions.parsers;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionsWarnHelper;
import ch.andre601.advancedserverlist.core.profiles.conditions.templates.ConstantExpressionTemplate;
import ch.andre601.advancedserverlist.core.profiles.conditions.templates.ExpressionTemplate;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.BooleanToken;
@@ -35,7 +36,7 @@ import java.util.List;
public class BooleanConstantReader extends ValueReader{
@Override
public ExpressionTemplate read(ExpressionTemplateParser parser, List<Token> tokenList){
public ExpressionTemplate read(ExpressionTemplateParser parser, List<Token> tokenList, ExpressionsWarnHelper warnHelper){
if(tokenList.get(0) instanceof BooleanToken){
return ConstantExpressionTemplate.of(((BooleanToken)tokenList.remove(0)).getValue());
}
@@ -25,9 +25,9 @@
package ch.andre601.advancedserverlist.core.profiles.conditions.parsers;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionsWarnHelper;
import ch.andre601.advancedserverlist.core.profiles.conditions.operators.ListOperator;
import ch.andre601.advancedserverlist.core.profiles.conditions.operators.Operator;
import ch.andre601.advancedserverlist.core.profiles.conditions.templates.ExpressionErrorTemplate;
import ch.andre601.advancedserverlist.core.profiles.conditions.templates.ExpressionTemplate;
import ch.andre601.advancedserverlist.core.profiles.conditions.templates.ExpressionTemplates;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.Token;
@@ -47,32 +47,36 @@ public class ExpressionTemplateParser{
this.valueReaders = valueReaders;
}
public ExpressionTemplate parse(List<Token> tokenList){
public ExpressionTemplate parse(List<Token> tokenList, ExpressionsWarnHelper warnHelper){
List<ExpressionTemplate> parts = new ArrayList<>();
List<Operator> operators = new ArrayList<>();
try{
parts.add(read(tokenList));
parts.add(read(tokenList, warnHelper));
}catch(IllegalArgumentException ex){
return ExpressionErrorTemplate.of(ex.getMessage());
warnHelper.appendWarning(-1, ex.getMessage());
return null;
}
while(!tokenList.isEmpty()){
Token token = tokenList.remove(0);
Operator operator = this.operators.get(token);
if(operator == null){
return ExpressionErrorTemplate.of("Error while parsing Expression. Got \"" + token + "\" but expected OPERATOR.");
warnHelper.appendWarning(-1, "Received \"%s\" but expected OPERATOR.", token);
return null;
}
operators.add(operator);
if(tokenList.isEmpty()){
return ExpressionErrorTemplate.of("Received unexpected end of input.");
warnHelper.appendWarning(-1, "Received unexpected end of input.");
return null;
}
try{
parts.add(read(tokenList));
parts.add(read(tokenList, warnHelper));
}catch(IllegalArgumentException ex){
return ExpressionErrorTemplate.of(ex.getMessage());
warnHelper.appendWarning(-1, ex.getMessage());
return null;
}
}
@@ -121,9 +125,9 @@ public class ExpressionTemplateParser{
return parts.get(0);
}
ExpressionTemplate read(List<Token> tokenList){
ExpressionTemplate read(List<Token> tokenList, ExpressionsWarnHelper warnHelper){
for(ValueReader valueReader : valueReaders){
ExpressionTemplate template = valueReader.read(this, tokenList);
ExpressionTemplate template = valueReader.read(this, tokenList, warnHelper);
if(template != null)
return template;
}
@@ -25,6 +25,7 @@
package ch.andre601.advancedserverlist.core.profiles.conditions.parsers;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionsWarnHelper;
import ch.andre601.advancedserverlist.core.profiles.conditions.templates.ExpressionTemplate;
import ch.andre601.advancedserverlist.core.profiles.conditions.templates.ExpressionTemplates;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.Token;
@@ -40,10 +41,10 @@ public class NegatedExpressionReader extends ValueReader{
}
@Override
public ExpressionTemplate read(ExpressionTemplateParser parser, List<Token> tokenList){
public ExpressionTemplate read(ExpressionTemplateParser parser, List<Token> tokenList, ExpressionsWarnHelper warnHelper){
if(tokenList.get(0) == tokenNegation){
tokenList.remove(0);
return ExpressionTemplates.negate(parser.read(tokenList));
return ExpressionTemplates.negate(parser.read(tokenList, warnHelper));
}
return null;
@@ -25,6 +25,7 @@
package ch.andre601.advancedserverlist.core.profiles.conditions.parsers;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionsWarnHelper;
import ch.andre601.advancedserverlist.core.profiles.conditions.templates.ExpressionTemplate;
import ch.andre601.advancedserverlist.core.profiles.conditions.templates.ExpressionTemplates;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.Token;
@@ -40,10 +41,10 @@ public class NegatedNumberReader extends ValueReader{
}
@Override
public ExpressionTemplate read(ExpressionTemplateParser parser, List<Token> tokenList){
public ExpressionTemplate read(ExpressionTemplateParser parser, List<Token> tokenList, ExpressionsWarnHelper warnHelper){
if(tokenList.get(0) == tokenNegation){
tokenList.remove(0);
return ExpressionTemplates.negateNumber(parser.read(tokenList));
return ExpressionTemplates.negateNumber(parser.read(tokenList, warnHelper));
}
return null;
@@ -25,6 +25,7 @@
package ch.andre601.advancedserverlist.core.profiles.conditions.parsers;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionsWarnHelper;
import ch.andre601.advancedserverlist.core.profiles.conditions.templates.ConstantExpressionTemplate;
import ch.andre601.advancedserverlist.core.profiles.conditions.templates.ExpressionTemplate;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.NumberToken;
@@ -34,7 +35,7 @@ import java.util.List;
public class NumberConstantReader extends ValueReader{
@Override
public ExpressionTemplate read(ExpressionTemplateParser parser, List<Token> tokenList){
public ExpressionTemplate read(ExpressionTemplateParser parser, List<Token> tokenList, ExpressionsWarnHelper warnHelper){
if(tokenList.get(0) instanceof NumberToken){
return ConstantExpressionTemplate.of(((NumberToken)tokenList.remove(0)).getValue());
}
@@ -25,6 +25,7 @@
package ch.andre601.advancedserverlist.core.profiles.conditions.parsers;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionsWarnHelper;
import ch.andre601.advancedserverlist.core.profiles.conditions.templates.ExpressionTemplate;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.Token;
@@ -42,7 +43,7 @@ public class ParenthesisedExpressionReader extends ValueReader{
}
@Override
public ExpressionTemplate read(ExpressionTemplateParser parser, List<Token> tokenList){
public ExpressionTemplate read(ExpressionTemplateParser parser, List<Token> tokenList, ExpressionsWarnHelper warnHelper){
if(tokenList.get(0) == openingParenthesis){
int index = 0;
int cnt = 1;
@@ -60,7 +61,7 @@ public class ParenthesisedExpressionReader extends ValueReader{
}
}while(cnt != 0);
ExpressionTemplate result = parser.parse(new ArrayList<>(tokenList.subList(1, index)));
ExpressionTemplate result = parser.parse(new ArrayList<>(tokenList.subList(1, index)), warnHelper);
tokenList.subList(0, index + 1).clear();
return result;
@@ -25,6 +25,7 @@
package ch.andre601.advancedserverlist.core.profiles.conditions.parsers;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionsWarnHelper;
import ch.andre601.advancedserverlist.core.profiles.conditions.templates.ExpressionTemplate;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.PlaceholderToken;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.Token;
@@ -33,7 +34,7 @@ import java.util.List;
public class PlaceholderReader extends ValueReader{
@Override
public ExpressionTemplate read(ExpressionTemplateParser parser, List<Token> tokenList){
public ExpressionTemplate read(ExpressionTemplateParser parser, List<Token> tokenList, ExpressionsWarnHelper warnHelper){
if(tokenList.get(0) instanceof PlaceholderToken){
PlaceholderToken token = (PlaceholderToken)tokenList.remove(0);
return token.getValue();
@@ -25,6 +25,7 @@
package ch.andre601.advancedserverlist.core.profiles.conditions.parsers;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionsWarnHelper;
import ch.andre601.advancedserverlist.core.profiles.conditions.templates.ConstantExpressionTemplate;
import ch.andre601.advancedserverlist.core.profiles.conditions.templates.ExpressionTemplate;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.StringToken;
@@ -34,7 +35,7 @@ import java.util.List;
public class StringConstantReader extends ValueReader{
@Override
public ExpressionTemplate read(ExpressionTemplateParser parser, List<Token> tokenList){
public ExpressionTemplate read(ExpressionTemplateParser parser, List<Token> tokenList, ExpressionsWarnHelper warnHelper){
if(tokenList.get(0) instanceof StringToken){
return ConstantExpressionTemplate.of(((StringToken)tokenList.remove(0)).getValue());
}
@@ -25,6 +25,7 @@
package ch.andre601.advancedserverlist.core.profiles.conditions.parsers;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionsWarnHelper;
import ch.andre601.advancedserverlist.core.profiles.conditions.templates.ExpressionTemplate;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.Token;
@@ -32,5 +33,5 @@ import java.util.List;
public abstract class ValueReader{
public abstract ExpressionTemplate read(ExpressionTemplateParser parser, List<Token> tokenList);
public abstract ExpressionTemplate read(ExpressionTemplateParser parser, List<Token> tokenList, ExpressionsWarnHelper warnHelper);
}
@@ -33,30 +33,41 @@ import ch.andre601.advancedserverlist.core.profiles.conditions.templates.Express
public interface Placeholder extends ExpressionTemplate{
static LiteralPlaceholderValue of(String value){
return new LiteralPlaceholderValue(value);
double doubleValue;
try{
doubleValue = Double.parseDouble(value);
}catch(NumberFormatException ex){
doubleValue = value.length();
}
return new LiteralPlaceholderValue(value, doubleValue, Boolean.parseBoolean(value));
}
class LiteralPlaceholderValue implements Placeholder{
private final String value;
private final String stringValue;
private final double doubleValue;
private final boolean booleanValue;
public LiteralPlaceholderValue(String value){
this.value = value;
public LiteralPlaceholderValue(String stringValue, double doubleValue, boolean booleanValue){
this.stringValue = stringValue;
this.doubleValue = doubleValue;
this.booleanValue = booleanValue;
}
@Override
public ToStringExpression instantiateWithStringResult(){
return ToStringExpression.literal(value);
return ToStringExpression.literal(stringValue);
}
@Override
public ToDoubleExpression instantiateWithDoubleResult(){
return ToDoubleExpression.literal(value.length());
return ToDoubleExpression.literal(doubleValue);
}
@Override
public ToBooleanExpression instantiateWithBooleanResult(){
return ToBooleanExpression.literal(Boolean.parseBoolean(value));
return ToBooleanExpression.literal(booleanValue);
}
}
}
@@ -29,12 +29,13 @@ 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;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionsWarnHelper;
import java.text.ParsePosition;
public class PlaceholderParser{
public static Placeholder parse(String text, ParsePosition position, GenericPlayer player, GenericServer server){
public static Placeholder parse(String text, ParsePosition position, GenericPlayer player, GenericServer server, ExpressionsWarnHelper warnHelper){
AdvancedServerListAPI api = AdvancedServerListAPI.get();
int index = position.getIndex();
@@ -77,6 +78,7 @@ public class PlaceholderParser{
if(identified)
raw.append(' ').append(valuesStr);
warnHelper.appendWarning(index, "Placeholder '%s' does not have a closing bracket (}).",raw.toString());
return Placeholder.of(raw.toString());
}
@@ -91,6 +93,8 @@ public class PlaceholderParser{
raw.append('}');
warnHelper.appendWarning(index, "Placeholder '%s' does not have an available PlaceholderProvider.", raw.toString());
return Placeholder.of(raw.toString());
}
@@ -103,6 +107,8 @@ public class PlaceholderParser{
raw.append('}');
warnHelper.appendWarning(index, "Placeholder '%s' returned a null value.", raw.toString());
return Placeholder.of(raw.toString());
}
@@ -66,6 +66,18 @@ public class ExpressionTemplates{
return new NotEqualIgnoreCase(a, b);
}
public static ExpressionTemplate startsWith(ExpressionTemplate a, ExpressionTemplate b){
return new StartsWith(a, b);
}
public static ExpressionTemplate endsWith(ExpressionTemplate a, ExpressionTemplate b){
return new EndsWith(a, b);
}
public static ExpressionTemplate contains(ExpressionTemplate a, ExpressionTemplate b){
return new Contains(a, b);
}
public static ExpressionTemplate greater(ExpressionTemplate a, ExpressionTemplate b){
return new Greater(a, b);
}
@@ -222,6 +234,54 @@ public class ExpressionTemplates{
}
}
private static class StartsWith extends AbstractBooleanExpressionTemplate{
private final ExpressionTemplate a;
private final ExpressionTemplate b;
private StartsWith(ExpressionTemplate a, ExpressionTemplate b){
this.a = a;
this.b = b;
}
@Override
public ToBooleanExpression instantiateWithBooleanResult(){
return Expressions.startsWith(a.instantiateWithStringResult(), b.instantiateWithStringResult());
}
}
private static class EndsWith extends AbstractBooleanExpressionTemplate{
private final ExpressionTemplate a;
private final ExpressionTemplate b;
private EndsWith(ExpressionTemplate a, ExpressionTemplate b){
this.a = a;
this.b = b;
}
@Override
public ToBooleanExpression instantiateWithBooleanResult(){
return Expressions.endsWith(a.instantiateWithStringResult(), b.instantiateWithStringResult());
}
}
private static class Contains extends AbstractBooleanExpressionTemplate{
private final ExpressionTemplate a;
private final ExpressionTemplate b;
private Contains(ExpressionTemplate a, ExpressionTemplate b){
this.a = a;
this.b = b;
}
@Override
public ToBooleanExpression instantiateWithBooleanResult(){
return Expressions.contains(a.instantiateWithStringResult(), b.instantiateWithStringResult());
}
}
private static class Greater extends AbstractBooleanExpressionTemplate{
private final ExpressionTemplate a;
@@ -34,6 +34,9 @@ public class DefaultTokens{
public static final Token NOT_EQUAL = new Token("NOT_EQUAL");
public static final Token EQUAL_IGNORE_CASE = new Token("EQUAL_IGNORE_CASE");
public static final Token NOT_EQUAL_IGNORE_CASE = new Token("NOT_EQUAL_IGNORE_CASE");
public static final Token STARTS_WITH = new Token("STARTS_WITH");
public static final Token ENDS_WITH = new Token("ENDS_WITH");
public static final Token CONTAINS = new Token("CONTAINS");
public static final Token NEGATION = new Token("NEGATION");
public static final Token GREATER_THAN = new Token("GREATER_THAN");
public static final Token GREATER_OR_EQUAL_THAN = new Token("GREATER_OR_EQUAL_THAN");
@@ -27,6 +27,7 @@ package ch.andre601.advancedserverlist.core.profiles.conditions.tokens.readers;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionsWarnHelper;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.StringToken;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.Token;
@@ -39,7 +40,7 @@ public class NonQuotedLiteralTokenReader extends TokenReader{
}
@Override
public Token read(String text, ParsePosition position, GenericPlayer player, GenericServer server){
public Token read(String text, ParsePosition position, GenericPlayer player, GenericServer server, ExpressionsWarnHelper warnHelper){
int startIndex = position.getIndex();
int index = position.getIndex();
@@ -27,6 +27,7 @@ package ch.andre601.advancedserverlist.core.profiles.conditions.tokens.readers;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionsWarnHelper;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.NumberToken;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.Token;
@@ -43,7 +44,7 @@ public class NumberTokenReader extends TokenReader{
}
@Override
public Token read(String text, ParsePosition position, GenericPlayer player, GenericServer server){
public Token read(String text, ParsePosition position, GenericPlayer player, GenericServer server, ExpressionsWarnHelper warnHelper){
int previous = position.getIndex();
Number number = format.parse(text, position);
@@ -27,6 +27,7 @@ package ch.andre601.advancedserverlist.core.profiles.conditions.tokens.readers;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionsWarnHelper;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.Token;
import java.text.ParsePosition;
@@ -50,7 +51,7 @@ public class PatternTokenReader extends TokenReader{
}
@Override
public Token read(String text, ParsePosition position, GenericPlayer player, GenericServer server){
public Token read(String text, ParsePosition position, GenericPlayer player, GenericServer server, ExpressionsWarnHelper warnHelper){
if(text.regionMatches(ignoreCase, position.getIndex(), pattern, 0, pattern.length())){
position.setIndex(position.getIndex() + pattern.length());
return token;
@@ -27,6 +27,7 @@ package ch.andre601.advancedserverlist.core.profiles.conditions.tokens.readers;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionsWarnHelper;
import ch.andre601.advancedserverlist.core.profiles.conditions.placeholders.PlaceholderParser;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.PlaceholderToken;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.Token;
@@ -40,10 +41,10 @@ public class PlaceholderTokenReader extends TokenReader{
}
@Override
public Token read(String text, ParsePosition position, GenericPlayer player, GenericServer server){
public Token read(String text, ParsePosition position, GenericPlayer player, GenericServer server, ExpressionsWarnHelper warnHelper){
if(position.getIndex() + 1 < text.length() && text.charAt(position.getIndex()) == '$' && text.charAt(position.getIndex() + 1) == '{'){
position.setIndex(position.getIndex() + 2);
return new PlaceholderToken(PlaceholderParser.parse(text, position, player, server));
return new PlaceholderToken(PlaceholderParser.parse(text, position, player, server, warnHelper));
}
return null;
}
@@ -27,6 +27,7 @@ package ch.andre601.advancedserverlist.core.profiles.conditions.tokens.readers;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionsWarnHelper;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.StringToken;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.Token;
@@ -43,7 +44,7 @@ public class QuotedLiteralTokenReader extends TokenReader{
}
@Override
public Token read(String text, ParsePosition position, GenericPlayer player, GenericServer server){
public Token read(String text, ParsePosition position, GenericPlayer player, GenericServer server, ExpressionsWarnHelper warnHelper){
if(position.getIndex() < text.length() && text.charAt(position.getIndex()) == quote){
int startIndex = position.getIndex();
@@ -27,6 +27,7 @@ package ch.andre601.advancedserverlist.core.profiles.conditions.tokens.readers;
import ch.andre601.advancedserverlist.api.objects.GenericPlayer;
import ch.andre601.advancedserverlist.api.objects.GenericServer;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionsWarnHelper;
import ch.andre601.advancedserverlist.core.profiles.conditions.tokens.Token;
import java.text.ParsePosition;
@@ -43,5 +44,5 @@ public abstract class TokenReader{
return priority;
}
public abstract Token read(String text, ParsePosition position, GenericPlayer player, GenericServer server);
public abstract Token read(String text, ParsePosition position, GenericPlayer player, GenericServer server, ExpressionsWarnHelper warnHelper);
}
@@ -45,7 +45,7 @@ public class ProfileManager{
*/
public static ProfileEntry merge(ServerListProfile profile){
ProfileEntry entry = profile.getRandomProfile();
ProfileEntry defEntry = profile.getDefaultProfile();
ProfileEntry defEntry = profile.defaultProfile();
List<String> motd = resolveMOTD(entry, defEntry);
List<String> players = resolvePlayers(entry, defEntry);
@@ -26,10 +26,13 @@
package ch.andre601.advancedserverlist.test;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionEngine;
import ch.andre601.advancedserverlist.core.profiles.conditions.expressions.ExpressionsWarnHelper;
import org.junit.jupiter.api.Test;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ExpressionParserTests{
private final ExpressionEngine expressionEngine = new ExpressionEngine();
@@ -41,8 +44,10 @@ public class ExpressionParserTests{
for(Map.Entry<String, Boolean> values : testValuesMap.entrySet()){
logger.info("TEST[expression=\"%s\", expected=%b]", values.getKey(), values.getValue());
boolean result = expressionEngine.compile(values.getKey(), logger, null, null).instantiateWithBooleanResult().evaluate();
ExpressionsWarnHelper warnHelper = new ExpressionsWarnHelper(values.getKey());
boolean result = expressionEngine.compile(values.getKey(), null, null, warnHelper).instantiateWithBooleanResult().evaluate();
logger.info("Result (output, isExpected): " + result + ", " + (result == values.getValue()));
assertEquals(result, values.getValue());
}
}
-4
View File
@@ -9,7 +9,6 @@
from __future__ import annotations
import posixpath
import re
from mkdocs.config.defaults import MkDocsConfig
@@ -39,9 +38,6 @@ def on_page_markdown(
elif type == "deprecated": return _admo(text = args, type = "deprecated")
else: return _admo(text = args)
# Otherwise, raise an error
raise RuntimeError(f"Unknown shortcode: {type}")
# Find and replace all external asset URLs in current page
return re.sub(
r"<!-- admo:(\w+)(.*?) -->",
+26
View File
@@ -0,0 +1,26 @@
#
# A simple hook that replaces any appearances of \| with just |.
#
# There is an inconsistency between GitHub flavourited Markdown and Python Markdown
# where the former treats | as part of a table, even if it is inside inline code
# causing unwanted table rendering.
#
# The only way to prevent this is to escape it by prefixing it with a back slash (\).
# Unfortunately does this cause issues on Python-Markdown, as it renders inline code
# content as-is without special treatments whatsoever, meaning that `\|` will be
# rendered as <code>\|</code> in the end.
#
# Using this hook, `\|` is being turned into `|` before further processing, resulting
# in <code>|</code> being rendered in the end, while preserving proper table rendering
# on GitHub and co.
#
import re
from mkdocs.config.defaults import MkDocsConfig
from mkdocs.structure.files import File, Files
from mkdocs.structure.pages import Page
def on_page_markdown(
markdown: str, *, page: Page, config: MkDocsConfig, files: Files
):
return re.sub(r"\\\|","|",markdown)
+2 -1
View File
@@ -6,4 +6,5 @@ nav:
- examples
- migration
- api
- PlaceholderAPI: placeholderapi
- addons
- PlaceholderAPI: placeholderapi
+44
View File
@@ -0,0 +1,44 @@
# BanPlugins Addon
The BanPlugins Addon was made to provide placeholders for different punishment plugins to use in AdvancedServerList.
## Placeholders
### AdvancedBan
Placeholders for the plugin [AdvancedBan](https://www.spigotmc.org/resources/8695/){ target="_blank" rel="nofollow" } are available to use.
| Placeholder | Description |
|---------------------------------|-------------------------------------------------------------------------------------------|
| `${advancedban isMuted}` | Whether the player is muted or not. |
| `${advancedban muteReason}` | The reason for why the player was muted. Returns invalid placeholder if no mute was set. |
| `${advancedban muteDuration}` | Relative time until the mute expires.[^1] Returns invalid placeholder if no mute was set. |
| `${advancedban muteExpiration}` | Date on when the mute will expire.[^2] |
| `${advancedban isBanned}` | Whether the player is banned or not. |
| `${advancedban banReason}` | The reason for why the player was banned. Returns invalid placeholder if no ban was set. |
| `${advancedban banDuration}` | Relative time until the ban expires.[^3] Returns invalid placeholder if no ban was set. |
| `${advancedban banExpiration}` | Date on when the ban will expire.[^4] |
[^1]:
An optional boolean can be added to set whether the duration should be from the start or not. `false` is the default.
Example: `${advancedban muteDuration true}`
[^2]:
An optional Date and time Pattern can be set to change the displayed output. The default is `dd, MMM yyyy HH:mm:ss`.
Example: `${advancedban muteExpiration yyyy-MM-dd HH:mm:ss}`
[^3]:
An optional boolean can be added to set whether the duration should be from the start or not. `false` is the default.
Example: `${advancedban banDuration true}`
[^4]:
An optional Date and time Pattern can be set to change the displayed output. The default is `dd, MMM yyyy HH:mm:ss`.
Example: `${advancedban banExpiration yyyy-MM-dd HH:mm:ss}`
## Example
```yaml title="banned.yml"
priority: 1
condition: '${advancedban isBanned}'
motd:
- '<red>You were <bold>BANNED</bold>!'
- '<red>Expires: <grey>${advancedban banExpiration}'
```
+28
View File
@@ -0,0 +1,28 @@
---
icon: octicons/plus-circle-24
---
# Addons
This section contains a curated list of add-ons for AdvancedServerList which can be used to enhance the plugin in different ways.
Do you have a plugin that hooks into AdvancedServerList that is not listed here? Contact Andre601 on Discord and it will be added!
## First-party Addons
These Addons are made by Andre601 and available together with the plugin downloads.
<div class="grid cards" markdown>
- [**BanPlugins**](banplugins.md)
----
Addon for different Punishment plugins such as AdvancedBan, that add new placeholders to use in AdvancedServerList.
</div>
## Third-party
Third-party addons made by other people not working on AdvancedServerList.
*Currently no entries*
+1 -1
View File
@@ -40,7 +40,7 @@ document$.subscribe(async () => {
const versionToken = '{version}'
const entries = document.querySelectorAll('.md-content code');
for(const entry of entries) {
entry.innerHTML = entry.innerHTML.replace(new RegExp(versionToken, 'g'), version);
entry.innerHTML = entry.innerHTML.replace(new RegExp(versionToken, 'g'), version.substring(1));
}
}
+25 -4
View File
@@ -17,7 +17,9 @@ Feel free to suggest additional examples on my [Discord Server](https://discord.
- [Versions](#versions)
- [Different List for outdated Clients](#different-list-for-outdated-clients)
- [Different MOTD for < 1.16](#different-motd-for-116)
- [Spigot/PaperMC](#spigotpapermc)
- [BungeeCord/Velocity](#bungeecordvelocity)
- [Display MOTD for a set of subdomains](#display-motd-for-a-set-of-subdomains)
- [PaperMC](#papermc)
- [Different profile for banned Player](#different-profile-for-banned-player)
- [Other](#other)
- [Different profile for different host](#different-profile-for-different-host)
@@ -64,9 +66,28 @@ motd:
```
----
## Spigot/PaperMC
## BungeeCord/Velocity
Examples that can be used with the Spigot or PaperMC version.
Examples usable on the BungeeCord and Velocity version.
### Display MOTD for a set of subdomains
Displays the same MOTDs for the server when pinged on any sub-domain starting with `lobby` (i.e. `lobby-1.example.com`).
```yaml title="lobby-motd.yml"
priority: 1
condition: '${server host} |- "lobby"'
motd:
- '<green>Lobby Server'
- '<grey>Example Server'
```
----
## PaperMC
Examples that can be used with the PaperMC version.
### Different profile for banned Player
@@ -114,7 +135,7 @@ Display a different profile, if the player pinging the server is "known" by Adva
```yaml title="personalized.yml"
priority: 1
condition: '${player name} != Anonymous' # "Anonymous" would be the value from the "unknownPlayer > name" option.
condition: '${player name} != "Anonymous"' # "Anonymous" would be the value from the "unknownPlayer > name" option.
motd:
- '<grey>Hello there <aqua>${player name}</aqua>!'
+85 -27
View File
@@ -9,19 +9,40 @@ It tries to be an easy to understand as possible. But if you still have problems
## 1. Download the plugin { #download-the-plugin }
The first step would be to download the plugin from a download page of your choice. As of writing this is AdvancedServerList updated and available on these sites:
The first step would be to download the plugin from a download page of your choice.
AdvancedServerList is available on the following places (Please note the current status!):
- [Modrinth]
- [Codeberg]
<div class="grid cards" markdown>
- [:simple-modrinth: **Modrinth**][modrinth]
----
:octicons-check-24:{ style="color: var(--md-badge-fg--success);" } **Actively updated!**
- [:simple-codeberg: **Codeberg**][codeberg]
----
:octicons-check-24:{ style="color: var(--md-badge-fg--success);" } **Actively updated!**
- [:fontawesome-solid-paper-plane: **HangarMC**][hangar]
----
:octicons-check-24:{ style="color: var(--md-badge-fg--success);" } **Actively updated!**
- [:simple-spigotmc: **SpigotMC**][spigot]
----
:octicons-alert-24:{ style="color: var(--md-badge-fg--warning);" } **Discontinued!**
</div>
[modrinth]: https://modrinth.com/plugin/advancedserverlist
[codeberg]: https://codeberg.org/Andre601/AdvancedServerList
The plugin is also available on these platforms, but not actively updated there anymore:
- [Spigot] (Latest version: `3.3.0-b1`)
- [Hangar] (Latest version: `3.5.0`)
[spigot]: https://www.spigotmc.org/resources/102910/
[hangar]: https://hangar.papermc.io/Andre_601/AdvancedServerList
@@ -29,10 +50,44 @@ The plugin is available for Paper, BungeeCord, Waterfall and Velocity, and has b
The plugin also supports these additional plugins. They are all optional.
- [PlaceholderAPI] - Used on the Spigot and Paper server for additional Placeholder parsing.
- [ViaVersion] - Used on the Paper server for additional protocol version handling.
- [PAPIProxyBridge] - Used on BungeeCord, Waterfall and Velocity for PlaceholderAPI support.
- [Maintenance] - Used to disable server list handling while maintenance is active.
<div class="grid cards" markdown>
- [**PlaceholderAPI**][placeholderapi]
----
Supported on:
- :fontawesome-solid-paper-plane: Paper
- [**ViaVersion**][viaversion]
----
Supported on:
- :fontawesome-solid-paper-plane: Paper
- [**PAPIProxyBridge**][papiproxybridge]
----
Supported on:
- :simple-spigotmc: BungeeCord
- :fontawesome-solid-paper-plane: Velocity
- [**Maintenance**][maintenance]
----
Supported on:
- :fontawesome-solid-paper-plane: Paper
- :simple-spigotmc: BungeeCord
- :fontawesome-solid-paper-plane: Velocity
</div>
[placeholderapi]: https://hangar.papermc.io/HelpChat/PlaceholderAPI
[viaversion]: https://hangar.papermc.io/ViaVersion/ViaVersion
@@ -85,13 +140,12 @@ On first creation will the folder have a `default.yml` file. This file contains
priority: 0
#
# Set conditions that needs to be met.
# Only when all conditions return true will the profile be shown (Given no profile with higher priority is used).
# Set conditions that need to me met to show this profile.
# You can use keywords 'and' or 'or' (Or alternatively '&&' or `||`) to make more complex condition sets.
#
# Read more: https://asl.andre601.ch/profiles/#conditions
# Read more: https://asl.andre601.ch/profiles/#condition
#
#conditions:
# - '{playerVersion} < 759'
# condition: '${player protocol} < 759'
#
# Option that allows you to define multiple profiles to use.
@@ -322,17 +376,17 @@ config-version: 2
There are certain files and folders that get created when certain events happen:
- A `playercache.json` file will be created on plugin shutdown containing a list of IPs, UUIDs and Player names for every player that joined the server while AdvancedServerList was running.
This file allows to replace `${player name}` and `${player uuid}` with their name or UUID respectively, while they are not on the server, by using the IP they used to join with.
This file is not created nor updated when `disableCache` is set to `true` in the config.yml
This file allows to replace `${player name}` and `${player uuid}` with their name or UUID respectively, while they are not on the server, by using the IP they used to join with.
This file is not created nor updated when `disableCache` is set to `true` in the config.yml
- A `backups` folder containing old config.yml files.
This folder is created when AdvancedServerList migrates your old config to a new version. This allows you to transfer settings over in case the migration didn't work properly.
This folder is created when AdvancedServerList migrates your old config to a new version. This allows you to transfer settings over in case the migration didn't work properly.
## 3. Creating your first profile { #creating-your-first-profile }
Creating your first profile is relatively simple.
To start, open the `default.yml` file located inside the `profiles` folder using a file editor of your choice (VSCode or Notepad++ are recommended). It should contain all the available options for a server list profile.
Next can you edit the options available to whatever you like. All text options (Except [conditions](../profiles/index.md#conditions)) support [MiniMessage formatting](../profiles/formatting.md).
Next can you edit the options available to whatever you like. All text options (Except [condition](../profiles/index.md#condition)) support [MiniMessage formatting](../profiles/formatting.md).
If you're unsure how a specific option should look like, head over to the [Profiles page](../profiles/index.md) for more information about the general structure.
All you need to know is, that a bare-bones server list profile requires a valid [priority](../profiles/index.md#priority) and at least one of the settings to be present.
@@ -351,16 +405,20 @@ motd:
To add additional profiles, create a new YAML file inside the `profiles` folder. The name doesn't matter, but it is recommended to keep it lowercase, use alphanumeric characters (`a-z, 0-9`), hyphens (`-`), underscores (`_`) and avoid spaces.
Inside the file, add a priority and at least one setting, similar to the above shown example.
This would already be enough and AdvancedServerList would use it. If you however want to only show it when certain conditions are met, should you set the priority to a higher value and add conditions.
This would already be enough to have a valid profile, but depending on the priority and file name may the file not be used, or used over other profiles.
Conditions can be multiple strings containing [operands](../profiles/index.md#operands) to evaluate them. As an example `${server playersOnline} >= 10` would return true if the current number of online players is larger or equal to 10.
If you want to only show this file under specific situations can you use the [`condition`](../profiles/index.md#condition) option to apply conditions that need to be met to show the profile.
The condition option would have a single String containing one or muliple [Expressions](../profiles/expressions.md). Only if the condition would return true, would the profile be displayed, granted that it has a higher priority than any other valid file with a true condition output.
Here is another example profile using conditions:
/// info | Note
Profiles which do not have any conditions, or have an empty condition, will always considered to have a true condition, meaning that a profile with higher priority and no condition will be selected over a profile with lower priority and a condition, even if said condition returns true.
///
Here is another example profile using conditions to show it when the player was banned on the server:
```yaml title="other_profile.yml"
priority: 1
conditions:
- '${player isBanned} = true'
condition: '${player isBanned}'
motd:
- '<red>You got <bold>banned</bold> from this server.'
+7
View File
@@ -0,0 +1,7 @@
nav:
- index.md
- v3_8_0-to-v3_9_0.md
- v3_7_0-to-v3_8_0.md
- pre-v3_7_0-to-v3_7_0.md
- v1_9_0-to-v1_10_0.md
- v1_5_3-to-v1_6_0.md
+27 -1
View File
@@ -10,4 +10,30 @@ Note that these pages only cover server list profile files. The config.yml is au
## Pages
{nav}
<div class="grid cards" markdown>
- [**v3.7.0 to v3.8.0**](v3_7_0-to-v3_8_0.md)
----
Migrating from version 3.7.0 to 3.8.0.
- [**< v3.7.0 to v3.7.0**](pre-v3_7_0-to-v3_7_0.md)
----
Migrate from any version prior to 3.7.0 to 3.7.0 or newer.
- [**v1.9.0 to v1.10.0**](v1_9_0-to-v1_10_0.md)
----
Migrate from version 1.9.0 to 1.10.0.
- [**v1.5.3 to v1.6.0**](v1_5_3-to-v1_6_0.md)
----
Migrate from version 1.5.3 to 1.6.0.
</div>
@@ -2,7 +2,7 @@
icon: octicons/arrow-right-24
---
# < 3.7.0 to 3.7.0
# < v3.7.0 to v3.7.0
Version 3.7.0 of AdvancedServerList brought significant changes to its conditions system.
Namely, the `conditions` option is now deprecated and planned for removal in a future release of the Plugin, and the new [`condition`](../profiles/index.md#condition) option should now be used instead.
+49
View File
@@ -0,0 +1,49 @@
---
icon: octicons/arrow-right-24
---
# v3.7.0 to v3.8.0
v3.8.0 brought a small yet important change to the condition system, which is the change of the "contains" operand from `<-` to `<_` to avoid possible issues with numbers.
## Migration
To migrate, switch any appearances of `<-` with `<_`
## Example
Here is a before and after example to help visualize the change:
/// tab | Before
```yaml
priority: 0
conditions: '${server host} <- "survival"'
motd:
- 'Welcome ${player name}!'
```
///
/// tab | After
```yaml
priority: 0
condition: '${server host} <_ "survival"'
motd:
- 'Welcome ${player name}!'
```
///
/// tab | Diff View
```diff
priority: 0
-condition: '${server host} <- "survival"'
+condition: '${server host} <_ "survival"'
motd:
- 'Welcome ${player name}!'
```
///
+49
View File
@@ -0,0 +1,49 @@
---
icon: octicons/arrow-right-24
---
# v3.8.0 to v3.9.0
v3.9.0 brings a small change to the `~=` operand by changing it to `=~` for sake of consistent pattern design. Additionally, was `~` added as a single-character equivalent to `=`.
## Migration
To migrate, switch any appearances of `~=` with `=~` or `~`.
## Example
Here is a before and after example to help visualize the change:
/// tab | Before
```yaml
priority: 0
conditions: '${player name} ~= "Someone"'
motd:
- 'Welcome ${player name}!'
```
///
/// tab | After
```yaml
priority: 0
condition: '${player name} =~ "Someone"'
motd:
- 'Welcome ${player name}!'
```
///
/// tab | Diff View
```diff
priority: 0
-condition: '${player name} ~= "Someone"'
+condition: '${player name} =~ "Someone"'
motd:
- 'Welcome ${player name}!'
```
///
+135
View File
@@ -0,0 +1,135 @@
---
icon: octicons/file-code-24
---
Expressions are part of a Server List Profile's condition system and allows you to define when a profile should and should not be displayed.
This page covers what the Expression system of AdvancedServerList offers and can do.
## Content
- [Types](#types)
- [Literals](#literals)
- [Numbers](#numbers)
- [Strings](#strings)
- [Booleans](#booleans)
- [Placeholders](#placeholders)
- [Binary Operators](#binary-operators)
- [Parenthesis](#parenthesis)
- [Negation](#negation)
- [Credits](#credits)
## Types
An expression contains different types of components that are evaluated to return a boolean output of either `true` or `false`.
Depending on the type(s) will the output be differently understood and handled.
### Literals
The following cases are considered literal values:
#### Numbers
Any number is considered an expression, meaning the below examples are considered valid:
/// example | Examples
```
0
1000
-7
47.2
```
///
#### Strings
A String in single or double quotes is a valid expression.
/// warning | Important
Specific keywords will be treated as [Binary Operators](#binary-operators) even if they are part of a word.
As an example, `Sand` would cause the expression engine to see `S AND` due to parts of the word matching `and`.
To avoid this is it very important to always surround Strings with single or double quotes, as that tells the expression engine to treat as a String.
///
/// example | Examples
```
"Hello World!"
""
```
///
#### Booleans
Strings `true` and `false` are treated as boolean literals.
### Placeholders
Any [placeholder](placeholders.md) is a valid expression.
/// example | Examples
```
${player protocol}
${player name}
${server playersOnline}
${server host}
```
///
### Binary Operators
`<expression> <binary operator> <expression>` is a valid expression.
The following binary operators can be used for boolean evaluation:
| Operator | True if |
|---------------|-------------------------------------------------------------------------------------------------------------|
| `and` / `&&` | Both expressions return true. |
| `or` / `\|\|` | Either expression returns true. |
| `==` / `=` | Both expressions are equal (Case sensitive). |
| `=~` / `~` | Both expressions are equal (Not Case sensitive). |
| `!=` | Both expressions are not equal (Case sensitive). |
| `!~` | Both expressions are not equal (Not case sensitive). |
| `\|-` | Left expression starts with right expression. |
| `-\|` | Left expression ends with right expression. |
| `<_` | Left expression contains right expression. |
| `<` | Left expression is less than the right expression. Strings use their text length as number. |
| `<=` | Left expression is less than, or equal to, the right expression. Strings use their text length as number. |
| `>` | Left expression is larger than the right expression. Strings use their text length as number. |
| `>=` | Left expression is larger than, or equal to, the right expression. Strings use their text length as number. |
These additional binary operators can also be used to perform certain actions:
| Operator | Semantic |
|----------|-----------------------------------------------------------------------------|
| `.` | Concatenates (merges) two Strings |
| `+` | Adds two numbers. Strings use their text length as number. |
| `-` | Subtracts one number from another. Strings use their text length as number. |
| `*` | Multiplies two numbers. Strings use their text length as number. |
| `/` | Divides one number by another. Strings use their text length as number. |
/// example | Examples
```
${player name} != "Anonymous"
763 > ${player protocol} > 758
${player hasPlayedBefore} and ${player isBanned} == "false"
```
///
### Parenthesis
`( <expression> )` is a valid expression.
Parenthesis can be used to prevent ambiguities.
### Negation
`!` can be used to negate boolean expressions.
```
!${player isWhitelisted}
```
## Credits
This page, including some examples used, was adobted from [:octicons-book-24: CodeCrafter47/BungeeTabListPlus/wiki](https://github.com/CodeCrafter47/BungeeTabListPlus/wiki/Expressions){ target="_blank" rel="nofollow" }.
+12 -2
View File
@@ -38,8 +38,18 @@ The following options are **not** supported, no matter what option they are used
## Colors
Color options (`<aqua>`, `<red>`, `<gray>`, etc.) are available for all text options.
24-Bit HEX colors (`<#ff0000>`, `<#123abc>`, etc.) are only available for the [`Motd` option](../index.md#motd) for a 1.16+ Server/Proxy.
### Default colors
Default colors such as `<red>`, `<green>`, `<aqua>`, etc. may be used in all text options.
### Hex Colors
Hexadecimal colors may be used in the [`motd`](index.md#motd) option using the `<#RRGGBB>` format.
### Gradients
Gradients can be created by using `<gradient:<color1>:<color2>>`, replacing `<color1>` with a starting color name or hex color value and `<color2>` with an ending color name or hex color value.
Only the [`motd`](index.md#motd) option may support hex color gradients and in all other options will it be downsampled.
## Formatting
+90 -146
View File
@@ -2,115 +2,50 @@
icon: octicons/note-24
---
# Profiles { #profiles-settings }
# Profiles { #profiles-file }
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).
AdvancedServerList allows you to create multiple so called Server List Profiles.
A Server List Profile is a YAML file located in the plugin's `profiles` folder. Each Server List Profile allows you to modify one or multiple aspects of your server's display in a Multiplayer Server list.
/// info | Important
In order for a profile to be considered "valid" by AdvancedServerList will it need to meet the following conditions:
Additionally can [Conditions](#condition) and a [Priority](#priority) be set to set when a specific profile should be displayed to a player.
- 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
///
## Required Options
For a Server List Profile to be valid, will it require the following options to be set:
- A valid [`priority`](#priority) option with a single, whole number.
- **At least** one of the following options:
- [`profiles`](#profiles) with at least one of the below options set.
- [`motd`](#motd) with at least one line to display.
- [`favicon`](#favicon) with a non-empty String.
- [`playerCount -> hidePlayers`](#hideplayers) with value `true`.
- [`playerCount -> hover`](#hover) with at least one line to display.
- [`playerCount -> text`](#text) 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.
The Priority option allows you to define what Server List Profile should be chosen first.
The plugin will go highest value to lowest value and will select the first Profile that has a [`condition`](#condition) returning `true`.
It will go through each file until it finds one with [`condition`](#condition) 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.
Should AdvancedServerList not find any profile with a positive condition will it not modify the server list for the players.
/// note
It is important to note that should no [`condition`](#condition) option be set will it default to `true`, meaning that a profile with priority 1 and no condition will be chosen over a profile with priority 0 and a condition, even if said condition also returns true.
///
## Condition
<!-- admo:tip Surround any text that is not an operand or placeholder in single or double quotes to avoid parsing errors. -->
The `condition` option allows you to define specific requirements that need to be met to display the profile in question.
Not setting this option, or setting it to an empty String (`condition: ''`), will result in `true` being returned.
A string containing one or multiple conditions chained together using the keywords `and` or `or` (Or alternatively `&&` or `||`).
The system used for conditions is adobted from BungeeTabListPlus.
/// details | Example
type: example
```yaml title="YAML file"
priority: 0
# Checks if the player is using 1.20 or newer AND is whitelisted
condition: |
${player protocol} >= 763 and
${player isWhitelisted}
motd:
- 'Welcome ${player name}!'
```
///
### Operands { #condition-operands }
The following operands can be used for expressions in the condition option.
| Operand | Description |
|--------------|------------------------------------------------------------------------------------------------------------------|
| `and` / `&&` | Returns true if the expression on the left and right side return true. |
| `or` / `||` | Returns true if an expression from either side returns true. |
| `()` | Expressions in-between those are prioritized in evaluation, starting with the most inner first. |
| `<` | Returns true if the left side is less than the right. In case of text will the text length be used. |
| `<=` | Returns true if the left side is less or equal to the right. In case of text will the text length be used. |
| `>` | Returns true if the left side is larger than the right. In case of text will the text length be used. |
| `>=` | Returns true if the left side is larger or equal to the right. In case of text will the text length be used. |
| `==` / `=` | Returns true if both sides are equal to each other. This also checks capitalization. |
| `!=` | Returns true if both sides are NOT equal to each other. This also checks capitalization. |
| `~=` | Returns true if both sides are equal to each other while also ignoring capitalization. |
| `!~` | Returns true if both sides are NOT equal to each other while also ignoring capitalization. |
| `.` | Merges two strings into one. |
| `+` | Adds two numbers together. In case of text will its text length be used as number. |
| `-` | Subtracts the value from the right from the left number. In case of text will its text length be used as number. |
| `*` | Multiplies two numbers together. In case of text will its text length be used as number. |
| `/` | Divides the left number through the right number. In case of text will its text length be used as number. |
## Conditions :octicons-trash-24:{ title="Deprecated" style="color: #ff5252;" }
<!-- admo:deprecated This option was deprecated in favour of the new condition option! -->
A list of conditions can be set that need to be met in order to have this profile shown to the player.
[Placeholders](formatting.md#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.
### Operands { #conditions-operands }
The following operants are available and can be used in the conditions.
<!-- admo:warning For conditions option can you only use one operand per condition. -->
| Operand | Description |
|---------------|-------------------------------------------------------------------------|
| `<` | Checks if the left value is less than the right one.[^1] |
| `<=` | Checks if the left value is less than or equal to the right one.[^1] |
| `>` | Checks if the left value is more than the right one.[^1] |
| `>=` | Checks if the left value is more than or equal to the right one.[^1] |
| `=` | Checks if the left value is equal to the right one. |
| `!=` | Checks if the left value is not equal to the right one. |
| `~=` | Checks if the left value is equal to the right one, ignoring case. |
| `!~` | Checks if the left value is not equal to the right one, ignoring case. |
[^1]:
Should the provided value not be a number, will AdvancedServerList use the text length to compare.
Example: `SomeString > String` would be translated to `10 > 6`.
The condition option can hold one or multiple expressions. The Expression system is explained on the [Expressions page](expressions.md).
## Profiles
<!-- admo:warning Priority and Conditions can NOT be used within profiles -->
The `profile` option allows you to define a list of all other options, with the exception of [`priority`](#priority), [`condition`](#condition) or itself.
The `profiles` option allows you to set multiple MOTDs, player counts, etc. which the plugin would randomly choose from.
If an option such as motd is not present in an entry will it check the file for the option and use that if present.
When multiple entries are defined will AdvancedServerList randomly select one.
Should an option exist in the file itself (i.e. [`motd`](#motd)) but not in the selected profiles entry will it be used. This allows you to randomize only one aspect of a Server List Profile while keeping all other options the same.
/// details | Example
type: example
@@ -119,7 +54,7 @@ If an option such as motd is not present in an entry will it check the file for
priority: 0
profiles:
- motd:
- motd: # You can also use ['<rainbow>Line1</rainbow>','<rainbow:!>Line 2</rainbow>']
- '<rainbow>Line 1</rainbow>'
- '<rainbow:!>Line 2</rainbow>'
playerCount:
@@ -127,6 +62,7 @@ profiles:
- playerCount:
text: '<yellow><bold>Also Awesome!'
# This MOTD is used when the second profiles entry is selected.
motd:
- '<rainbow:2>Line A</rainbow>'
- '<rainbow:!2>Line B</rainbow>'
@@ -141,10 +77,10 @@ motd:
## 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>`.
The `motd` option allows you to alter the "Message of the day" that is being displayed on a server in the multiplayer screen.
Please see the [Formatting](formatting.md) for all supported formatting options.
If you want to display multiple MOTDs will you need to use the [`Profiles` option](#profiles)
Multiple, randomly selected, MOTDs can be displayed by using the [`profiles`](#profiles) option.
/// details | Example
type: example
@@ -153,7 +89,7 @@ If you want to display multiple MOTDs will you need to use the [`Profiles` optio
priority: 0
motd:
- '<rainbow:2>Rainbow Gradients</rainbow>'
- '<rainbow:2>Rainbow Gradient</rainbow>'
- '<rainbow:!2>|||||||||||||||||||||||||||||||||||||</rainbow>'
```
<div class="result" markdown>
@@ -165,53 +101,60 @@ motd:
## Favicon
The favicon option allows you to customize the favicon and display one or multiple different images.
<!-- admo:info Images will be resized to 64x64 pixels. -->
This option currently supports the following values:
The `favicon` option can be used to define an image to display in the server list.
- 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
The Value can be one of three possible options, each being checked for in order:
Please note that AdvancedServerList will rescale the image to be 64x64 pixels, so be sure to provide images at that particular scale.
- A URL pointing to an image file. The URL needs to start with `https://` to work.
- A PNG filename, including `.png` file extension, matching a PNG file in the `favicons` folder.
- A player name or UUID, or a [placeholder](placeholders.md) that resolves into one.
Note that for the name/UUID, the site https://mc-heads.net is being used.
Use the URL option with the `${player uuid}` placeholder if you want to use another service.
/// details | Example
type: example
```yaml title="YAML file"
priority: 0
# We use just colour to hide the MOTD
# Hides the MOTD for demonstration purposes
motd:
- '<grey>'
- '<grey>'
# Player is Andre_601
favicon: ${player uuid}
# Placeholder resolves to UUID of Andre_601
favicon: '${player uuid}'
```
<div class="result" markdown>
![hideplayers-example](../assets/images/examples/favicon-example.jpg){ loading="lazy" }
![favicon-example](../assets/images/examples/favicon-example.jpg){ loading="lazy" }
</div>
///
## PlayerCount
The `playerCount` option contains multiple different options all about the Player count itself.
The `playerCount` option contains various other options that are used to modify the player count (The text next to the ping icon that shows the online and max players of a server).
### 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.
The `playerCount -> hidePlayers` option takes a boolean (`true` or `false`) to set whether the player count should be hidden or not.
When set to true will the player count be replaced with `???`.
In addition are the [`playerCount -> text`](#text) and [`playerCount -> hover`](#hover) options ignored.
If you want to not show any text at all, set this option to `false` (or remove it) and instead use the [`playerCount -> text`](#text) option.
/// details | Example
type: example
```yaml title="YAML file"
priority: 0
# We use just colour to hide the MOTD
# Hides the MOTD for demonstration purposes
motd:
- '<grey>'
- '<grey>'
@@ -228,17 +171,17 @@ playerCount:
### 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.
The `playerCount -> hover` option allows you to define lines of text to show when a player hovers over the player count with their cursor.
Note that this option only accepts basic colors such as `<blue>` or `<grey>` and that HEX colors are not supported.
/// details | Example
type: example
```yaml title="YAML file"
priority: 0
# We use just colour to hide the MOTD
# Hides the MOTD for demonstration purposes
motd:
- '<grey>'
- '<grey>'
@@ -258,23 +201,24 @@ playerCount:
### 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.md#server) and [`${server playersMax}`](placeholders.md#server)
The `playerCount -> text` option allows you to define a custom text to display instead of the usual `<online>/<max>`.
/// 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.
///
Please take note of the following caveats:
To not override the player count, remove this option or set it to an empty String (`text: ''`).
- The plugin won't automatically add the `<online>/<max>` back to the text you set. Instead you'll have to use `${server playersOnline}/${server playersMax}` to recreate this.
- The override is done by using the "outdated server" text displayed when a client pings a server that is not compatible. This means that the ping icon will appear crossed out and your server will be seen as "outdated" by the client (When hovering over the ping icon). This can't be avoided nor fixed by the plugin.
If you want to not show any text, just use a single color (i.e. `text: '<grey>'`) to do so.
This option only supports basic colors and no HEX colors.
/// details | Example
type: example
```yaml title="YAML file"
priority: 0
# We use just colour to hide the MOTD
# Hides the MOTD for demonstration purposes
motd:
- '<grey>'
- '<grey>'
@@ -284,35 +228,37 @@ playerCount:
```
<div class="result" markdown>
![hover-example](../assets/images/examples/text-example.jpg){ loading="lazy" }
![text-example](../assets/images/examples/text-example.jpg){ loading="lazy" }
</div>
///
### ExtraPlayers
Contains options to modify how the `<max>` number in the player count should look like.
<!-- admo:warning Using this option overrides the output of the <code>${server playersMax}</code> placeholder (Except in condition). -->
<!-- admo:warning Using this option will modify the <code>${server playersMax}</code> placeholder (Except when used in conditions) -->
The `playerCount -> extraPlayers` option contains settings for the "extra players" feature, which adds X to the current online player count and sets it as the server's max player count.
#### Enabled { #extraplayers-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.
Enables the extraPlayers feature.
Use the [`playerCount -> extraPlayers -> amount`](#extraplayers-amount) option to set how much to add to the max players count.
#### Amount { #extraplayers-amount }
Sets the number to add to the current online players to then use as the new max players count.
<!-- admo:info This option does nothing when <code>playerCount -> hidePlayers</code> is enabled. -->
As an example, setting this to `1` while `10` players are online will display `11`, while `-1` would display `9`.
The `playerCount -> extraPlayers -> amount` option defines how much should be added to the current online player count to then use as max player count.
Example: Setting this option to `1` while currently 10 players are online will result in `10/11` being displayed on the player count (Assuming it is not being overriden by the [`playerCount -> text`](#text) option).
/// details | Example
type: example
```yaml title="YAML file"
priority: 0
# We use just colour to hide the MOTD
# Hides the MOTD for demonstration purposes
motd:
- '<grey>'
- '<grey>'
@@ -331,32 +277,30 @@ playerCount:
### MaxPlayers
Contains options to set the max number of players allowed to join the server. This has only a visual effect and won't actually influence how many players can join your server.
<!-- admo:info This feature is ignored should <code>playerCount -> extraPlayers</code> be enabled. -->
<!-- admo:warning Using this option overrides the output of the <code>${server playersMax}</code> placeholder (Except in condition). -->
Note that this feature is ignored should [`extraPlayers`](#extraplayers) be enabled.
In case [`hidePlayers`](#hideplayers) is enabled will the max player count not be displayed, but still changed by this option.
Should [`text`](#text) be set to a non-empty String will the max player count only be displayed when `${server playersMax}` is used.
<!-- admo:warning Using this option will modify the <code>${server playersMax}</code> placeholder (Except when used in conditions) -->
The `playerCount -> maxPlayers` option is similar in nature to the [`playerCount -> extraPlayers`](#extraplayers) option, with the difference that the number set is being used as the max player count, instead of adding it together with the online player count first.
#### Enabled { #maxplayers-enabled }
Boolean option to set whether the max players feature should be used or not.
When set to `true` will the value usually displaying how many players can join be changed to what you've set in the [`amount`](#maxplayers-amount) option.
Enables the maxPlayers feature.
Use the [`playerCount -> maxPlayers -> amount`](#maxplayers-amount) option to set the max player count itself.
#### Amount { #maxplayers-amount }
Sets the number to display as the max number of players allowed to join your server.
Note that this will also affect the `${server playersMax}` placeholder, except when it is used inside conditions.
<!-- admo:info This option does nothing when <code>playerCount -> hidePlayers</code> is enabled. -->
The `playerCount -> maxPlayers -> amount` option defines the number to use as the server's max players count.
Unlike the [`playerCount -> extraPlayers -> amount`](#extraplayers-amount) option does this one not first add the current online player count to the number to then set.
/// details | Example
type: example
```yaml title="YAML file"
priority: 0
# We use just colour to hide the MOTD
# Hides the MOTD for demonstration purposes
motd:
- '<grey>'
- '<grey>'
@@ -368,7 +312,7 @@ playerCount:
```
<div class="result" markdown>
![maxplayes-example](../assets/images/examples/maxplayers-example.jpg){ loading="lazy" }
![maxplayers-example](../assets/images/examples/maxplayers-example.jpg){ loading="lazy" }
</div>
///
+12 -12
View File
@@ -21,9 +21,9 @@ These placeholders use the player who pinged the server, to return values. They
| `${player protocol}` | The protocol version of the player. | All | No |
| `${player uuid}` | The UUID of the player. | All | Yes[^3] |
| `${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 |
| `${player hasPlayedBefore}` | Boolean for whether the player has played on the server before. | Paper | Yes |
| `${player isBanned}` | Boolean for whether the player has been banned from the server. | Paper | Yes |
| `${player isWhitelisted}` | Boolean for whether the player is whitelisted on the server. | Paper | Yes |
[^1]:
Whether this placeholder requires the player to have joined before while AdvancedServerList was running.
@@ -35,21 +35,21 @@ These placeholders use the player who pinged the server, to return values. They
These placeholders use values given by the server/proxy AdvancedServerList runs on.
| Placeholder | Description |
|------------------------------|-------------------------------------------------------------------------|
| `${server playersOnline}` | The number of players online on this proxy/server.[^4] |
| `${server playersMax}` | The total number of players that can join this server.[^5] |
| `${server host}` | The domain/IP the player pinged.[^6] |
| `${server whitelistEnabled}` | Whether the whitelist is enabled or not. Only available on Spigot/Paper |
| Placeholder | Description | Platforms |
|------------------------------|------------------------------------------------------------|-----------|
| `${server playersOnline}` | The number of players online on this proxy/server.[^4] | All |
| `${server playersMax}` | The total number of players that can join this server.[^5] | All |
| `${server host}` | The domain/IP the player pinged.[^6] | All |
| `${server whitelistEnabled}` | Whether the whitelist is enabled or not. | Paper |
[^4]:
A comma-separated list of world (On Paper) or Server (On BungeeCord/Velocity) names can be provided to display the added numbers of players in these worlds/servers.
**Example:** `${server playersOnline lobby1,lobby2}` will display the collective numbers of players in the servers `lobby1` and `lobby2`.
[^5]:
When the [`amount` option](../index.md#amount) is used will this placeholder display the modified max players count.
The only exception is [`conditions`](../index.md#conditions) where it uses the actual max players of the server/proxy.
Using either the [extraPlayers' `amount`](index.md#extraplayers-amount) or [maxPlayers' `amount`](index.md#maxplayers-amount) option will make this placeholder return the modified max players count.
Only exception to this rule is when the placeholder is used in the [`condition`](index.md#condition) option in which case it returns the actual max player count of the proxy/server.
[^6]:
An optional server name can be provided to display the IP/Domain associated with that server. Only works on BungeeCord/Velocity.
An optional server name can be provided to display the IP/Domain associated with that server. This only works on BungeeCord or Velocity.
**Example:** `${server host survival}` would display the IP/Domain associated with the `survival` server.
## PlaceholderAPI
+1
View File
@@ -106,3 +106,4 @@ markdown_extensions:
hooks:
- docs/.hooks/badges.py
- docs/.hooks/replacer.py
-8
View File
@@ -48,14 +48,6 @@
<id>papermc</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
</repository>
<repository>
<id>spigot</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>protocollib-repo</id>
<url>https://repo.dmulloy2.net/repository/public/</url>
</repository>
<repository>
<id>placeholderapi-repo</id>
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
@@ -1,60 +0,0 @@
/*
* MIT License
*
* Copyright (c) 2022-2023 Andre_601
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package ch.andre601.advancedserverlist.paper;
import io.papermc.paper.plugin.loader.PluginClasspathBuilder;
import io.papermc.paper.plugin.loader.PluginLoader;
import io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.repository.RemoteRepository;
import org.jetbrains.annotations.NotNull;
public class ASLLoader implements PluginLoader{
@Override
public void classloader(@NotNull PluginClasspathBuilder pluginClasspathBuilder){
ComponentLogger logger = pluginClasspathBuilder.getContext().getLogger();
logger.info(Component.text("Loading Libraries for plugin..."));
MavenLibraryResolver resolver = new MavenLibraryResolver();
resolver.addRepository(new RemoteRepository.Builder(
"paper-maven",
"default",
"https://repo.papermc.io/repository/maven-public/"
).build());
logger.info(Component.text("Loading com.squareup.okhttp3:okhttp:4.10.0 from repo.papermc.io..."));
resolver.addDependency(new Dependency(new DefaultArtifact("com.squareup.okhttp3:okhttp:4.10.0"), null));
pluginClasspathBuilder.addLibrary(resolver);
logger.info(Component.text("Loading libraries complete!"));
}
}
@@ -171,11 +171,11 @@ public class PaperCore extends JavaPlugin implements PluginCore<CachedServerIcon
getPluginLogger().warn(" - Reliance on a different plugin for core functionalities.");
getPluginLogger().warn(" - Bad dependency loading limited to MavenCentral only.");
getPluginLogger().warn("");
getPluginLogger().warn("Please consider switching to Paper or one of the Paper-compatible forks to benefit from");
getPluginLogger().warn("overall performance improvements amongst other things.");
getPluginLogger().warn("Please consider switching to Paper or one of the Paper-compatible forks to benefit");
getPluginLogger().warn("from overall performance improvements amongst other things.");
getPluginLogger().warn("");
getPluginLogger().warn("AdvancedServerList will now disable itself. If you believe this was a mistake, report");
getPluginLogger().warn("this on Codeberg: https://codeberg.org/Andre601/AdvancedServerList");
getPluginLogger().warn("Disabling AdvancedServerList. If you believe this is an error, report it on");
getPluginLogger().warn("Codeberg: https://codeberg.org/Andre601/AdvancedServerList");
getPluginLogger().warn("======================================================================================");
Bukkit.getPluginManager().disablePlugin(this);
@@ -4,7 +4,6 @@ version: '${plugin.version}'
description: '${plugin.description}'
main: 'ch.andre601.advancedserverlist.paper.PaperCore'
loader: 'ch.andre601.advancedserverlist.paper.ASLLoader'
api-version: '1.19'
+2 -1
View File
@@ -38,7 +38,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.commit/>
<plugin.version>3.7.0-b2</plugin.version>
<plugin.version>4.0.0</plugin.version>
<plugin.description>Create multiple Server lists based on conditions.</plugin.description>
<api.version>v3.1.0</api.version>
@@ -60,5 +60,6 @@
<module>paper</module>
<module>velocity</module>
<module>version-uploader</module>
<module>addons/banplugins</module>
</modules>
</project>
+2 -2
View File
@@ -1,4 +1,4 @@
mkdocs-material==9.5.4
mkdocs-glightbox==0.3.6
mkdocs-material==9.5.9
mkdocs-glightbox==0.3.7
mkdocs-awesome-pages-plugin==2.9.2
git+https://github.com/Andre601/mkdocs-pagenav-generator
-10
View File
@@ -81,12 +81,6 @@
<version>4.1.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
@@ -114,10 +108,6 @@
<pattern>org.bstats</pattern>
<shadedPattern>ch.andre601.advancedserverlist.velocity.depends.bstats</shadedPattern>
</relocation>
<relocation>
<pattern>okhttp3</pattern>
<shadedPattern>ch.andre601.advancedserverlist.velocity.depends.okhttp3</shadedPattern>
</relocation>
</relocations>
<filters>
<filter>
+2 -2
View File
@@ -52,13 +52,13 @@
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.14</version>
<version>1.5.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3</version>
<version>5.3.1</version>
<scope>compile</scope>
</dependency>
<dependency>
@@ -31,27 +31,35 @@ public enum PlatformInfo{
PAPER(
"paper",
"paper/target/AdvancedServerList-Paper-{{version}}.jar",
List.of(
"paper/target/AdvancedServerList-Paper-{{version}}.jar",
"addons/banplugins/target/AdvancedServerList-BanPlugins-Addon-{{version}}.jar"
),
"paper", "folia"
),
BUNGEECORD(
"bungeecord",
"bungeecord/target/AdvancedServerList-BungeeCord-{{version}}.jar",
List.of(
"bungeecord/target/AdvancedServerList-BungeeCord-{{version}}.jar",
"addons/banplugins/target/AdvancedServerList-BanPlugins-Addon-{{version}}.jar"
),
"bungeecord", "waterfall"
),
VELOCITY(
"velocity",
"velocity/target/AdvancedServerList-Velocity-{{version}}.jar",
List.of(
"velocity/target/AdvancedServerList-Velocity-{{version}}.jar"
),
"velocity"
);
private final String platform;
private final String filePath;
private final List<String> filePaths;
private final List<String> loaders;
PlatformInfo(String platform, String filePath, String... loaders){
PlatformInfo(String platform, List<String> filePaths, String... loaders){
this.platform = platform;
this.filePath = filePath;
this.filePaths = filePaths;
this.loaders = List.of(loaders);
}
@@ -59,8 +67,8 @@ public enum PlatformInfo{
return platform;
}
public String getFilePath(){
return filePath;
public List<String> getFilePaths(){
return filePaths;
}
public List<String> getLoaders(){
@@ -90,9 +90,9 @@ public class HangarVersionUploader{
boolean preRelease = release.prerelease();
final List<Path> filePaths = List.of(
new File(PlatformInfo.PAPER.getFilePath().replace("{{version}}", pluginVersion)).toPath(),
new File(PlatformInfo.BUNGEECORD.getFilePath().replace("{{version}}", pluginVersion)).toPath(),
new File(PlatformInfo.VELOCITY.getFilePath().replace("{{version}}", pluginVersion)).toPath()
new File(PlatformInfo.PAPER.getFilePaths().get(0).replace("{{version}}", pluginVersion)).toPath(),
new File(PlatformInfo.BUNGEECORD.getFilePaths().get(0).replace("{{version}}", pluginVersion)).toPath(),
new File(PlatformInfo.VELOCITY.getFilePaths().get(0).replace("{{version}}", pluginVersion)).toPath()
);
final List<Dependency> paperDependencies = List.of(
@@ -123,7 +123,7 @@ public class HangarVersionUploader{
Platform.VELOCITY, velocityDependencies
),
Map.of(
Platform.PAPER, List.of("1.19.x", "1.20.x"),
Platform.PAPER, List.of("1.20.x"),
Platform.WATERFALL, List.of("1.19.x", "1.20.x"),
Platform.VELOCITY, Collections.singletonList("3.2")
),
@@ -38,8 +38,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
public class ModrinthVersionUploader{
@@ -52,7 +52,6 @@ public class ModrinthVersionUploader{
PlatformInfo.VELOCITY
);
private final List<String> versions = List.of(
"1.19", "1.19.1", "1.19.2", "1.19.3", "1.19.4",
"1.20", "1.20.1", "1.20.2", "1.20.3", "1.20.4"
);
@@ -82,37 +81,48 @@ public class ModrinthVersionUploader{
final boolean prerelease = release.prerelease();
CompletableFuture<?>[] futures = new CompletableFuture[platforms.size()];
List<ProjectVersion.ProjectDependency> paperDependencies = List.of(
new ProjectVersion.ProjectDependency(null, "VCAqN1ln", null, ProjectVersion.ProjectDependencyType.OPTIONAL), // Maintenance
new ProjectVersion.ProjectDependency(null, "P1OZGk5p", null, ProjectVersion.ProjectDependencyType.OPTIONAL) // ViaVersion
);
List<ProjectVersion.ProjectDependency> bungeeDependencies = List.of(
new ProjectVersion.ProjectDependency(null, "VCAqN1ln", null, ProjectVersion.ProjectDependencyType.OPTIONAL), // Maintenance
new ProjectVersion.ProjectDependency(null, "bEIUEGTX", null, ProjectVersion.ProjectDependencyType.OPTIONAL) // PAPIProxyBridge
);
List<ProjectVersion.ProjectDependency> velocityDependencies = List.of(
new ProjectVersion.ProjectDependency(null, "VCAqN1ln", null, ProjectVersion.ProjectDependencyType.OPTIONAL), // Maintenance
new ProjectVersion.ProjectDependency(null, "bEIUEGTX", null, ProjectVersion.ProjectDependencyType.OPTIONAL) // PAPIProxyBridge
);
for(int i = 0; i < platforms.size(); i++){
LOGGER.info("Creating release for platform {}...", platforms.get(i).getPlatform());
File file = new File(platforms.get(i).getFilePath().replace("{{version}}", pluginVersion));
List<File> files = platforms.get(i).getFilePaths().stream()
.map(path -> new File(path.replace("{{version}}", pluginVersion)))
.filter(File::exists)
.toList();
List<ProjectVersion.ProjectDependency> dependencies = new ArrayList<>();
// Maintenance plugin
dependencies.add(new ProjectVersion.ProjectDependency(null, "VCAqN1ln", null, ProjectVersion.ProjectDependencyType.OPTIONAL));
// ViaVersion plugin
dependencies.add(new ProjectVersion.ProjectDependency(null, "P1OZGk5p", null, ProjectVersion.ProjectDependencyType.OPTIONAL));
if(!platforms.get(i).getPlatform().equalsIgnoreCase("paper")){
// PAPIProxyBridge plugin
dependencies.add(new ProjectVersion.ProjectDependency(null, "bEIUEGTX", null, ProjectVersion.ProjectDependencyType.OPTIONAL));
}
CreateVersion.CreateVersionRequest request = CreateVersion.CreateVersionRequest.builder()
CreateVersion.CreateVersionRequest.CreateVersionRequestBuilder builder = CreateVersion.CreateVersionRequest.builder()
.projectId("xss83sOY")
.name(String.format("v%s (%s)", releaseVersion, String.join(", ", platforms.get(i).getLoaders())))
.versionNumber(releaseVersion)
.changelog(changelog.replaceAll("\r\n", "\n"))
.featured(false)
.files(file)
.files(files)
.gameVersions(versions)
.loaders(platforms.get(i).getLoaders())
.versionType(prerelease ? ProjectVersion.VersionType.BETA : ProjectVersion.VersionType.RELEASE)
.dependencies(dependencies)
.build();
.versionType(prerelease ? ProjectVersion.VersionType.BETA : ProjectVersion.VersionType.RELEASE);
switch(platforms.get(i).getPlatform().toLowerCase(Locale.ROOT)){
case "paper" -> builder.dependencies(paperDependencies);
case "bungeecord" -> builder.dependencies(bungeeDependencies);
case "velocity" -> builder.dependencies(velocityDependencies);
}
if(dryrun){
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(request);
String json = gson.toJson(builder.build());
LOGGER.info("JSON Data to send: {}", json);
@@ -120,7 +130,7 @@ public class ModrinthVersionUploader{
continue;
}
futures[i] = api.versions().createProjectVersion(request).whenComplete(((projectVersion, throwable) -> {
futures[i] = api.versions().createProjectVersion(builder.build()).whenComplete(((projectVersion, throwable) -> {
if(throwable != null){
LOGGER.warn("Encountered an exception while uploading a new release to Modrinth!", throwable);
return;