Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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");
Expand Down
28 changes: 8 additions & 20 deletions src/main/java/lol/hyper/velocityblockversion/events/JoinEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,45 +23,33 @@
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();
if (configHandler.getConfig().getBoolean("log_connection_versions", false)) {
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 = "<red>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()
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -30,23 +36,45 @@
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
@DataDirectory
private Path folderPath;

private Toml config;
private final List<Integer> blockVersions = new ArrayList<>();
public final long CONFIG_VERSION = 5;
private OperationMode operationMode = OperationMode.BLACKLIST;
private Set<Integer> 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 {
Expand Down Expand Up @@ -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<Integer> 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<Integer> getBlockVersions() {
return blockVersions;
/**
* Get the set of version protocol included in the configuration by the user
* @return The version set
*/
public Set<Integer> 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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Integer> deniedVersions) {
final Map<Integer, ProtocolVersion> versionMap = new HashMap<>(ProtocolVersion.ID_TO_PROTOCOL_CONSTANT);
versionMap.remove(-1);
versionMap.remove(-2);
final List<Integer> allVersions = new ArrayList<>(versionMap.keySet());
allVersions.removeAll(deniedVersions);
if (allVersions.isEmpty()) {
return null;
public static String versionRange(final List<Integer> versionList) {
if (versionList.isEmpty()) {
return "{null}";
}

final int minVersion = Collections.min(allVersions);
final int maxVersion = Collections.max(allVersions);
List<VersionRange> ranges = new ArrayList<>();
List<Integer> supported = new ArrayList<>(ProtocolVersion.SUPPORTED_VERSIONS
.stream().map(ProtocolVersion::getProtocol).toList());

int start = supported.indexOf(versionList.get(0));
int prev = start;

Iterator<Integer> 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());
}
}
31 changes: 25 additions & 6 deletions src/main/resources/config.toml
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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 <versions> 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 <versions> 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 <versions>, just remove it.
disconnect_message = "<red>You cannot connect with this version! We only allow version(s) <versions>."
# You may need to change the default value if you use whitelist mode.
disconnect_message = "<red>The following version(s) are not allowed to join this server: <versions>."

# 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
config_version = 6
12 changes: 12 additions & 0 deletions src/main/resources/velocity-plugin.json
Original file line number Diff line number Diff line change
@@ -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"
}