From e1194070fe8ad684b4e399670ccbf6b8333e3ae7 Mon Sep 17 00:00:00 2001 From: Lander Gallastegi Date: Tue, 28 Oct 2025 02:40:39 +0100 Subject: [PATCH 1/6] Some enhancements --- .../events/JoinEvent.java | 24 ++----- .../tools/ConfigHandler.java | 69 ++++++++++++++++--- .../tools/VersionToStrings.java | 57 +++++++++++---- src/main/resources/config.toml | 28 ++++++-- 4 files changed, 130 insertions(+), 48 deletions(-) diff --git a/src/main/java/lol/hyper/velocityblockversion/events/JoinEvent.java b/src/main/java/lol/hyper/velocityblockversion/events/JoinEvent.java index 6b19042..35f34c7 100644 --- a/src/main/java/lol/hyper/velocityblockversion/events/JoinEvent.java +++ b/src/main/java/lol/hyper/velocityblockversion/events/JoinEvent.java @@ -23,13 +23,8 @@ 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; @@ -43,25 +38,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..e5543d2 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,8 +63,10 @@ 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() {} @@ -83,23 +107,30 @@ 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 -> { + versionsList.removeIf(protocol -> { if (ProtocolVersion.ID_TO_PROTOCOL_CONSTANT.containsKey(protocol)) { return false; } else { @@ -107,15 +138,33 @@ public boolean loadConfig() { return true; } }); - logger.info("Loaded {} versions!", blockVersions.size()); + versionsList.sort((a, b) -> a.compareTo(b)); + logger.info("Loaded {} versions!", versionsSet.size()); } - logger.info("Loaded versions: {}", blockVersions.stream().map(String::valueOf).collect(Collectors.joining(", "))); + logger.info("Loaded versions: {}", versionsSet.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; + public Set getVersionsSet() { + return versionsSet; + } + + public OperationMode getOperationMode() { + return operationMode; + } + + public Component getDeniedMessage() { + return deniedMessage; } public Toml getConfig() { diff --git a/src/main/java/lol/hyper/velocityblockversion/tools/VersionToStrings.java b/src/main/java/lol/hyper/velocityblockversion/tools/VersionToStrings.java index 13e8f20..cc737ec 100644 --- a/src/main/java/lol/hyper/velocityblockversion/tools/VersionToStrings.java +++ b/src/main/java/lol/hyper/velocityblockversion/tools/VersionToStrings.java @@ -22,26 +22,57 @@ 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; + } + + @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) { + List ranges = new ArrayList<>(); + + Integer start = null, prev = null; + + for (int version : versionList) { + if (start == null) { + start = version; + prev = version; + continue; + } + + if (version == prev + 1) { + prev = version; + } else if (version == prev) { + continue; + } else { + ranges.add(new VersionRange(start, prev)); + start = null; + prev = null; + } } - final int minVersion = Collections.min(allVersions); - final int maxVersion = Collections.max(allVersions); + if (start != null) { + ranges.add(new VersionRange(start, 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..fcfbba1 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,11 +43,23 @@ versions = [ 759, 760, 761, + 762, + 763, + 764, + 765, + 766, + 767, + 768, + 769, + 770, + 771, + 772, ] -# 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. +# You may need to change the default value if you use whitelist mode. disconnect_message = "You cannot connect with this version! We only allow version(s) ." # This will say "Player is connecting with protocol version" when someone joins. @@ -49,4 +67,4 @@ disconnect_message = "You cannot connect with this version! We only allow v log_connection_versions = false # No touch please :) -config_version = 5 \ No newline at end of file +config_version = 6 From 633f30781de16d113a05455ac1a1cfa7dece0f9d Mon Sep 17 00:00:00 2001 From: Lander Gallastegi Date: Tue, 28 Oct 2025 17:41:56 +0100 Subject: [PATCH 2/6] Fixes --- .../lol/hyper/velocityblockversion/tools/ConfigHandler.java | 4 ++-- src/main/resources/config.toml | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/lol/hyper/velocityblockversion/tools/ConfigHandler.java b/src/main/java/lol/hyper/velocityblockversion/tools/ConfigHandler.java index e5543d2..6925cf7 100644 --- a/src/main/java/lol/hyper/velocityblockversion/tools/ConfigHandler.java +++ b/src/main/java/lol/hyper/velocityblockversion/tools/ConfigHandler.java @@ -139,9 +139,9 @@ public boolean loadConfig() { } }); versionsList.sort((a, b) -> a.compareTo(b)); - logger.info("Loaded {} versions!", versionsSet.size()); + logger.info("Loaded {} versions!", versionsList.size()); } - logger.info("Loaded versions: {}", versionsSet.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"); diff --git a/src/main/resources/config.toml b/src/main/resources/config.toml index fcfbba1..1be94f8 100644 --- a/src/main/resources/config.toml +++ b/src/main/resources/config.toml @@ -54,13 +54,14 @@ versions = [ 770, 771, 772, + 773, ] # 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. # You may need to change the default value if you use whitelist mode. -disconnect_message = "You cannot connect with this version! We only allow version(s) ." +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. From d7a667082c8aa9dcbefdfa8bac2a31c2dafa038f Mon Sep 17 00:00:00 2001 From: Lander Gallastegi Date: Tue, 28 Oct 2025 18:27:53 +0100 Subject: [PATCH 3/6] More fixes and fix ranges --- .../tools/ConfigHandler.java | 10 ++------- .../tools/VersionToStrings.java | 21 +++++++++---------- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/main/java/lol/hyper/velocityblockversion/tools/ConfigHandler.java b/src/main/java/lol/hyper/velocityblockversion/tools/ConfigHandler.java index 6925cf7..8aad06f 100644 --- a/src/main/java/lol/hyper/velocityblockversion/tools/ConfigHandler.java +++ b/src/main/java/lol/hyper/velocityblockversion/tools/ConfigHandler.java @@ -130,15 +130,9 @@ public boolean loadConfig() { if (versionsList.isEmpty()) { logger.warn("There are no versions listed in the config!"); } else { - versionsList.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; - } - }); 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: {}", versionsList.stream().map(String::valueOf).collect(Collectors.joining(", "))); diff --git a/src/main/java/lol/hyper/velocityblockversion/tools/VersionToStrings.java b/src/main/java/lol/hyper/velocityblockversion/tools/VersionToStrings.java index cc737ec..de0e2fe 100644 --- a/src/main/java/lol/hyper/velocityblockversion/tools/VersionToStrings.java +++ b/src/main/java/lol/hyper/velocityblockversion/tools/VersionToStrings.java @@ -48,29 +48,28 @@ private VersionToStrings() {} */ public static String versionRange(final List versionList) { List ranges = new ArrayList<>(); - - Integer start = null, prev = null; + List supported = new ArrayList<>(ProtocolVersion.SUPPORTED_VERSIONS + .stream().map(ProtocolVersion::getProtocol).toList()); + + Integer start = null, prev = 0; for (int version : versionList) { if (start == null) { - start = version; - prev = version; + start = supported.indexOf(version); + prev = start; continue; } - if (version == prev + 1) { - prev = version; - } else if (version == prev) { - continue; + if (supported.get(prev + 1).equals(version)) { + prev += 1; } else { - ranges.add(new VersionRange(start, prev)); + ranges.add(new VersionRange(supported.get(start), supported.get(prev))); start = null; - prev = null; } } if (start != null) { - ranges.add(new VersionRange(start, prev)); + ranges.add(new VersionRange(supported.get(start), supported.get(prev))); } return String.join(", ", ranges.stream().map(VersionRange::toString).toList()); From 5b0ee9b1ee37bd61120f55c6c49588a31a8cd430 Mon Sep 17 00:00:00 2001 From: Lander Gallastegi Date: Tue, 28 Oct 2025 18:36:37 +0100 Subject: [PATCH 4/6] Add plugin manifest --- src/main/resources/velocity-plugin.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/resources/velocity-plugin.json 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" +} From 2ae7c5e67fd53570858736a60333a458f7c17184 Mon Sep 17 00:00:00 2001 From: Lander Gallastegi Date: Tue, 28 Oct 2025 19:54:08 +0100 Subject: [PATCH 5/6] Fix version string builder --- .../tools/VersionToStrings.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/java/lol/hyper/velocityblockversion/tools/VersionToStrings.java b/src/main/java/lol/hyper/velocityblockversion/tools/VersionToStrings.java index de0e2fe..cde5592 100644 --- a/src/main/java/lol/hyper/velocityblockversion/tools/VersionToStrings.java +++ b/src/main/java/lol/hyper/velocityblockversion/tools/VersionToStrings.java @@ -47,30 +47,30 @@ private VersionToStrings() {} * @return Returns the string of versions. Returns "{null}" if the input list is empty. */ public static String versionRange(final List versionList) { + if (versionList.isEmpty()) { + return "{null}"; + } + List ranges = new ArrayList<>(); List supported = new ArrayList<>(ProtocolVersion.SUPPORTED_VERSIONS .stream().map(ProtocolVersion::getProtocol).toList()); - - Integer start = null, prev = 0; - for (int version : versionList) { - if (start == null) { - start = supported.indexOf(version); - prev = start; - continue; - } + int start = supported.indexOf(versionList.get(0)), prev = start; - if (supported.get(prev + 1).equals(version)) { + 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 = null; + start = supported.indexOf(version); + prev = start; } } - - if (start != null) { - ranges.add(new VersionRange(supported.get(start), supported.get(prev))); - } + ranges.add(new VersionRange(supported.get(start), supported.get(prev))); return String.join(", ", ranges.stream().map(VersionRange::toString).toList()); } From e37b084ae4f50d6cea62cdd3769a711ff05cf5d6 Mon Sep 17 00:00:00 2001 From: Lander Gallastegi Date: Tue, 28 Oct 2025 20:11:47 +0100 Subject: [PATCH 6/6] Make DeepSource happy (I hope) --- .../VelocityBlockVersion.java | 7 +++++++ .../commands/CommandReload.java | 6 ++++++ .../events/JoinEvent.java | 4 ++++ .../tools/ConfigHandler.java | 20 +++++++++++++++++++ .../tools/VersionToStrings.java | 7 ++++++- 5 files changed, 43 insertions(+), 1 deletion(-) 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 35f34c7..a9231d8 100644 --- a/src/main/java/lol/hyper/velocityblockversion/events/JoinEvent.java +++ b/src/main/java/lol/hyper/velocityblockversion/events/JoinEvent.java @@ -31,6 +31,10 @@ public final class JoinEvent { @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(); diff --git a/src/main/java/lol/hyper/velocityblockversion/tools/ConfigHandler.java b/src/main/java/lol/hyper/velocityblockversion/tools/ConfigHandler.java index 8aad06f..1f2047e 100644 --- a/src/main/java/lol/hyper/velocityblockversion/tools/ConfigHandler.java +++ b/src/main/java/lol/hyper/velocityblockversion/tools/ConfigHandler.java @@ -71,6 +71,10 @@ public static OperationMode fromString(String mode) { @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 { @@ -149,18 +153,34 @@ public boolean loadConfig() { return true; } + /** + * 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 cde5592..c7650dc 100644 --- a/src/main/java/lol/hyper/velocityblockversion/tools/VersionToStrings.java +++ b/src/main/java/lol/hyper/velocityblockversion/tools/VersionToStrings.java @@ -31,6 +31,10 @@ private static class VersionRange { 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(); @@ -55,7 +59,8 @@ public static String versionRange(final List versionList) { List supported = new ArrayList<>(ProtocolVersion.SUPPORTED_VERSIONS .stream().map(ProtocolVersion::getProtocol).toList()); - int start = supported.indexOf(versionList.get(0)), prev = start; + int start = supported.indexOf(versionList.get(0)); + int prev = start; Iterator it = versionList.iterator(); it.next();