diff --git a/src/main/java/lol/hyper/velocityblockversion/VelocityBlockVersion.java b/src/main/java/lol/hyper/velocityblockversion/VelocityBlockVersion.java index 3db9b06..9566b6a 100644 --- a/src/main/java/lol/hyper/velocityblockversion/VelocityBlockVersion.java +++ b/src/main/java/lol/hyper/velocityblockversion/VelocityBlockVersion.java @@ -62,6 +62,10 @@ public class VelocityBlockVersion { @Inject private Injector injector; + /** + * Called by Velocity when the proxy is being initialized + * @param event The event context + */ @Subscribe public void onProxyInitialization(final ProxyInitializeEvent event) { final ConfigHandler configHandler = injector.getInstance(ConfigHandler.class); @@ -81,6 +85,9 @@ public void onProxyInitialization(final ProxyInitializeEvent event) { commandManager.register(meta, commandReload); } + /** + * Checks for available updates and informs the user by logging it to console. + */ public void checkForUpdates() { GitHubReleaseAPI api; try { diff --git a/src/main/java/lol/hyper/velocityblockversion/commands/CommandReload.java b/src/main/java/lol/hyper/velocityblockversion/commands/CommandReload.java index 47a5231..0bdba02 100644 --- a/src/main/java/lol/hyper/velocityblockversion/commands/CommandReload.java +++ b/src/main/java/lol/hyper/velocityblockversion/commands/CommandReload.java @@ -28,6 +28,9 @@ public final class CommandReload implements SimpleCommand { @Inject private ConfigHandler configHandler; + /** + * Called by Brigadier when the command is executed. + */ @Override public void execute(final Invocation invocation) { final CommandSource source = invocation.source(); @@ -36,6 +39,9 @@ public void execute(final Invocation invocation) { } } + /** + * Returns whether an invoacion has permission to execute this command. + */ @Override public boolean hasPermission(final Invocation invocation) { return invocation.source().hasPermission("velocityblockversion.reload"); diff --git a/src/main/java/lol/hyper/velocityblockversion/events/JoinEvent.java b/src/main/java/lol/hyper/velocityblockversion/events/JoinEvent.java index 6b19042..a9231d8 100644 --- a/src/main/java/lol/hyper/velocityblockversion/events/JoinEvent.java +++ b/src/main/java/lol/hyper/velocityblockversion/events/JoinEvent.java @@ -23,19 +23,18 @@ import com.velocitypowered.api.event.connection.PreLoginEvent; import com.velocitypowered.api.network.ProtocolVersion; import lol.hyper.velocityblockversion.tools.ConfigHandler; -import lol.hyper.velocityblockversion.tools.VersionToStrings; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import org.slf4j.Logger; -import static net.kyori.adventure.text.minimessage.MiniMessage.miniMessage; - public final class JoinEvent { @Inject private Logger logger; @Inject private ConfigHandler configHandler; + /** + * Called by Velocity when a player joins. + * @param event The context of the event. + */ @Subscribe(order = PostOrder.FIRST) public void onPlayerLogin(final PreLoginEvent event) { final int version = event.getConnection().getProtocolVersion().getProtocol(); @@ -43,25 +42,14 @@ public void onPlayerLogin(final PreLoginEvent event) { logger.info("Player is connecting with protocol version: {}", version); } - if (!configHandler.getBlockVersions().contains(version)) { + boolean isBlacklist = configHandler.getOperationMode().equals(ConfigHandler.OperationMode.BLACKLIST); + if (configHandler.getVersionsSet().contains(version) ^ isBlacklist) { return; } - String allowedVersions = VersionToStrings.allowedVersions(configHandler.getBlockVersions()); - String blockedMessage = configHandler.getConfig().getString("disconnect_message"); - - if (allowedVersions == null) { - blockedMessage = "All versions are currently blocked from playing."; - allowedVersions = ""; - } - - final Component message = miniMessage().deserialize( - blockedMessage, - Placeholder.unparsed("versions", allowedVersions) - ); - event.setResult(PreLoginEvent.PreLoginComponentResult.denied(message)); + event.setResult(PreLoginEvent.PreLoginComponentResult.denied(configHandler.getDeniedMessage())); logger.info( - "Blocking player {} because they are playing on version {} which is blocked!", + "Blocking player {} because they are playing on version {} which is not allowed!", event.getUsername(), ProtocolVersion.getProtocolVersion(version).getMostRecentSupportedVersion() ); diff --git a/src/main/java/lol/hyper/velocityblockversion/tools/ConfigHandler.java b/src/main/java/lol/hyper/velocityblockversion/tools/ConfigHandler.java index fc967c7..1f2047e 100644 --- a/src/main/java/lol/hyper/velocityblockversion/tools/ConfigHandler.java +++ b/src/main/java/lol/hyper/velocityblockversion/tools/ConfigHandler.java @@ -17,11 +17,17 @@ package lol.hyper.velocityblockversion.tools; +import com.google.common.collect.ImmutableSet; import com.google.inject.Inject; import com.google.inject.Singleton; import com.moandjiezana.toml.Toml; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.plugin.annotation.DataDirectory; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; + import org.slf4j.Logger; import java.io.IOException; @@ -30,10 +36,26 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; @Singleton public final class ConfigHandler { + public enum OperationMode { + BLACKLIST, WHITELIST; + + public static OperationMode fromString(String mode) { + switch (mode) { + case "blacklist": + return BLACKLIST; + case "whitelist": + return WHITELIST; + default: + return null; + } + } + }; + @Inject private Logger logger; @Inject @@ -41,12 +63,18 @@ public final class ConfigHandler { private Path folderPath; private Toml config; - private final List blockVersions = new ArrayList<>(); - public final long CONFIG_VERSION = 5; + private OperationMode operationMode = OperationMode.BLACKLIST; + private Set versionsSet = ImmutableSet.of(); + private Component deniedMessage = MiniMessage.miniMessage().deserialize(""); + public final long CONFIG_VERSION = 6; @Inject public ConfigHandler() {} + /** + * Load the plugin settings from the configuration file. + * @return True if the configuration was loaded, false if there was an error. + */ public boolean loadConfig() { if (Files.notExists(folderPath)) { try { @@ -83,41 +111,76 @@ public boolean loadConfig() { logger.warn( "To fix this, delete your current config and let the server remake it."); } - blockVersions.clear(); + operationMode = OperationMode.fromString(config.getString("mode", "blacklist")); + if (operationMode == null) { + logger.error("Unexpected mode option found. Using default value."); + operationMode = OperationMode.BLACKLIST; + } + logger.info("Operation mode: {}", operationMode.toString()); + + List versionsList = new ArrayList<>(); // for some reason, the config loads the versions as longs // we have to convert them this ugly way for (Object obj : config.getList("versions", List.of())) { if (obj instanceof Number) { int t = ((Number) obj).intValue(); - blockVersions.add(t); + versionsList.add(t); } else { logger.error("Unexpected versions configuration input {}", obj); } } - if (blockVersions.isEmpty()) { + if (versionsList.isEmpty()) { logger.warn("There are no versions listed in the config!"); } else { - blockVersions.removeIf(protocol -> { - if (ProtocolVersion.ID_TO_PROTOCOL_CONSTANT.containsKey(protocol)) { - return false; - } else { - logger.warn("Version {} is NOT a valid version number! Ignoring this version.", protocol); - return true; - } - }); - logger.info("Loaded {} versions!", blockVersions.size()); + versionsList.sort((a, b) -> a.compareTo(b)); + versionsList = versionsList.stream().filter(elm -> ProtocolVersion.ID_TO_PROTOCOL_CONSTANT.containsKey(elm)) + .distinct().sorted().toList(); + logger.info("Loaded {} versions!", versionsList.size()); } - logger.info("Loaded versions: {}", blockVersions.stream().map(String::valueOf).collect(Collectors.joining(", "))); + logger.info("Loaded versions: {}", versionsList.stream().map(String::valueOf).collect(Collectors.joining(", "))); + String versionString = VersionToStrings.versionRange(versionsList); + String disconnectMessage = config.getString("disconnect_message"); + + deniedMessage = MiniMessage.miniMessage().deserialize( + disconnectMessage, + Placeholder.unparsed("versions", versionString) + ); + + versionsSet = ImmutableSet.copyOf(versionsList); return true; } - public List getBlockVersions() { - return blockVersions; + /** + * Get the set of version protocol included in the configuration by the user + * @return The version set + */ + public Set getVersionsSet() { + return versionsSet; + } + + /** + * Get the mode on which the plugin operates. + * @return A value from the enum describing the mode. + */ + public OperationMode getOperationMode() { + return operationMode; + } + + /** + * Get the computed message sent to players that are denied access. + * @return miniMessage Component containing the message. + */ + public Component getDeniedMessage() { + return deniedMessage; } + /** + * Get the config parse instance. + * @return The Toml parser instance. + */ public Toml getConfig() { return config; } diff --git a/src/main/java/lol/hyper/velocityblockversion/tools/VersionToStrings.java b/src/main/java/lol/hyper/velocityblockversion/tools/VersionToStrings.java index 13e8f20..c7650dc 100644 --- a/src/main/java/lol/hyper/velocityblockversion/tools/VersionToStrings.java +++ b/src/main/java/lol/hyper/velocityblockversion/tools/VersionToStrings.java @@ -22,26 +22,61 @@ import java.util.*; public final class VersionToStrings { + private static class VersionRange { + int start; + int end; + + VersionRange(int start, int end) { + this.start = start; + this.end = end; + } + + /** + * Get the string representation of this version range + * @return A string representing the minimum and maximum version of this range. + */ + @Override + public String toString() { + String firstVersion = ProtocolVersion.ID_TO_PROTOCOL_CONSTANT.get(start).getVersionIntroducedIn(); + String lastVersion = ProtocolVersion.ID_TO_PROTOCOL_CONSTANT.get(end).getMostRecentSupportedVersion(); + return firstVersion.equals(lastVersion) ? firstVersion : firstVersion + " - " + lastVersion; + } + } + private VersionToStrings() {} /** * Builds a string that will show what versions the server supports. Example: 1.8 to 1.14.4 - * @param deniedVersions Versions to deny. - * @return Returns the string of versions. Returns nulls if there are no versions that are allowed. + * @param versionList The list of versions + * @return Returns the string of versions. Returns "{null}" if the input list is empty. */ - public static String allowedVersions(final List deniedVersions) { - final Map versionMap = new HashMap<>(ProtocolVersion.ID_TO_PROTOCOL_CONSTANT); - versionMap.remove(-1); - versionMap.remove(-2); - final List allVersions = new ArrayList<>(versionMap.keySet()); - allVersions.removeAll(deniedVersions); - if (allVersions.isEmpty()) { - return null; + public static String versionRange(final List versionList) { + if (versionList.isEmpty()) { + return "{null}"; } - final int minVersion = Collections.min(allVersions); - final int maxVersion = Collections.max(allVersions); + List ranges = new ArrayList<>(); + List supported = new ArrayList<>(ProtocolVersion.SUPPORTED_VERSIONS + .stream().map(ProtocolVersion::getProtocol).toList()); + + int start = supported.indexOf(versionList.get(0)); + int prev = start; + + Iterator it = versionList.iterator(); + it.next(); + + while (it.hasNext()) { + int version = it.next(); + if (version == supported.get(prev + 1)) { + prev += 1; + } else { + ranges.add(new VersionRange(supported.get(start), supported.get(prev))); + start = supported.indexOf(version); + prev = start; + } + } + ranges.add(new VersionRange(supported.get(start), supported.get(prev))); - return versionMap.get(minVersion).toString() + " to " + versionMap.get(maxVersion).toString(); + return String.join(", ", ranges.stream().map(VersionRange::toString).toList()); } } diff --git a/src/main/resources/config.toml b/src/main/resources/config.toml index 667db20..1be94f8 100644 --- a/src/main/resources/config.toml +++ b/src/main/resources/config.toml @@ -1,8 +1,14 @@ -# These versions will NOT be allowed to connect. +# Set the mode for the plugin. This can either be "blacklist" or "whitelist". +# blacklist: Versions listed below will not be allowed to connect. +# whitelist: Only versions listed below will be allowed to connect. +mode = "blacklist" + # These versions MUST be the version number. You can check the numbers here: https://wiki.vg/Protocol_version_numbers -# By default, all versions are listed here. +# By default, all versions from 1.7.2 to 1.21.10 are listed here. # Everything here must have a comma after it! versions = [ + 4, + 5, 47, 107, 108, @@ -37,16 +43,29 @@ versions = [ 759, 760, 761, + 762, + 763, + 764, + 765, + 766, + 767, + 768, + 769, + 770, + 771, + 772, + 773, ] -# Send this message if someone connects with a blocked version. -# Use to show what versions your server uses. It will display like "1.8 to 1.16.3" or whatever. +# Send this message when someone is denied access to the server. +# Use to show what versions your server uses. It will display like "1.8 - 1.16.3" or whatever. # If you don't want to use , just remove it. -disconnect_message = "You cannot connect with this version! We only allow version(s) ." +# You may need to change the default value if you use whitelist mode. +disconnect_message = "The following version(s) are not allowed to join this server: ." # This will say "Player is connecting with protocol version" when someone joins. # This is off by default to not spam your console, but you can enable it for debug reasons. log_connection_versions = false # No touch please :) -config_version = 5 \ No newline at end of file +config_version = 6 diff --git a/src/main/resources/velocity-plugin.json b/src/main/resources/velocity-plugin.json new file mode 100644 index 0000000..8f29ebf --- /dev/null +++ b/src/main/resources/velocity-plugin.json @@ -0,0 +1,12 @@ +{ + "id": "velocityblockversion", + "name": "VelocityBlockVersion", + "version": "1.0.9", + "description": "Block certain Minecraft versions from connecting to your network.", + "url": "https://github.com/hyperdefined/VelocityBlockVersion", + "authors": [ + "hyperdefined" + ], + "dependencies": [], + "main": "lol.hyper.velocityblockversion.VelocityBlockVersion" +}