Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e4423e2ce | ||
|
|
ddb65f2f5a | ||
|
|
254faecc90 | ||
|
|
582024eba1 | ||
|
|
e1fe738ffe | ||
|
|
5f3a439113 | ||
|
|
27cca23412 | ||
|
|
2d2c576ad4 | ||
|
|
0adf7b0b6a | ||
|
|
c6ff9370c2 |
No files matched your search
+6
-1
@@ -38,11 +38,16 @@ subprojects {
|
||||
testCompileOnly("org.projectlombok:lombok:1.18.30")
|
||||
testAnnotationProcessor("org.projectlombok:lombok:1.18.30")
|
||||
|
||||
compileOnly("io.netty:netty-all:4.1.98.Final") // netty
|
||||
// MiniMessage
|
||||
implementation("net.kyori:adventure-text-minimessage:4.14.0")
|
||||
implementation("net.kyori:adventure-text-serializer-gson:4.14.0")
|
||||
implementation("net.kyori:adventure-text-serializer-legacy:4.14.0")
|
||||
|
||||
implementation("net.kyori:adventure-nbt:4.14.0") // nbt
|
||||
implementation("com.j256.ormlite:ormlite-jdbc:6.1") // ORM
|
||||
implementation("xyz.jonesdev:cappuccino:0.1.6-SNAPSHOT") // expiring cache
|
||||
|
||||
compileOnly("io.netty:netty-all:4.1.99.Final") // netty
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
group=xyz.jonesdev.sonar
|
||||
version=2.0.1
|
||||
version=2.0.2
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -54,6 +54,11 @@ public interface Sonar {
|
||||
|
||||
@NotNull Verbose getVerboseHandler();
|
||||
|
||||
/**
|
||||
* Set a custom verbose handler
|
||||
*/
|
||||
void setVerboseHandler(final @NotNull Verbose verboseHandler);
|
||||
|
||||
/**
|
||||
* Reloads the entire plugin
|
||||
*/
|
||||
|
||||
@@ -24,7 +24,7 @@ import xyz.jonesdev.sonar.api.command.subcommand.Subcommand;
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public final class CommandInvocation {
|
||||
private final InvocationSender sender;
|
||||
private final InvocationSource sender;
|
||||
private final Subcommand command;
|
||||
private final String[] rawArguments;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Sonar Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.api.command;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public abstract class InvocationSource {
|
||||
private final String name;
|
||||
private final Audience audience;
|
||||
|
||||
/**
|
||||
* Sends an empty chat message to the command executor
|
||||
*/
|
||||
public final void sendMessage() {
|
||||
sendMessage(Component.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the command executor
|
||||
*/
|
||||
public final void sendMessage(final String legacy) {
|
||||
sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(legacy));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the command executor
|
||||
*/
|
||||
public final void sendMessage(final Component component) {
|
||||
audience.sendMessage(component);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Sonar Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.api.command;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import xyz.jonesdev.cappuccino.Cappuccino;
|
||||
import xyz.jonesdev.cappuccino.ExpiringCache;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public interface SonarCommand {
|
||||
List<String> TAB_SUGGESTIONS = new ArrayList<>();
|
||||
|
||||
Map<String, List<String>> ARG_TAB_SUGGESTIONS = new HashMap<>();
|
||||
|
||||
ExpiringCache<Object> DELAY = Cappuccino.buildExpiring(500L);
|
||||
|
||||
int COPYRIGHT_YEAR = Calendar.getInstance().get(Calendar.YEAR);
|
||||
|
||||
List<Component> CACHED_HELP_MESSAGE = new Vector<>();
|
||||
|
||||
default void cacheHelpMessage() {
|
||||
CACHED_HELP_MESSAGE.addAll(Arrays.asList(
|
||||
Component.text("Running Sonar " + Sonar.get().getVersion()
|
||||
+ " on " + Sonar.get().getServer().getPlatform().getDisplayName()
|
||||
+ ".", NamedTextColor.YELLOW),
|
||||
Component.text("(C) " + COPYRIGHT_YEAR + " Jones Development and Sonar Contributors", NamedTextColor.YELLOW),
|
||||
Component.text("https://github.com/jonesdevelopment/sonar", NamedTextColor.GREEN)
|
||||
.clickEvent(net.kyori.adventure.text.event.ClickEvent.clickEvent(net.kyori.adventure.text.event.ClickEvent.Action.OPEN_URL, "https://github.com/jonesdevelopment/sonar")),
|
||||
Component.empty(),
|
||||
Component.text("Need help or have any questions?", NamedTextColor.YELLOW),
|
||||
Component.textOfChildren(
|
||||
Component.text("Open a ticket on the Discord ", NamedTextColor.YELLOW)
|
||||
.hoverEvent(net.kyori.adventure.text.event.HoverEvent.hoverEvent(net.kyori.adventure.text.event.HoverEvent.Action.SHOW_TEXT, Component.text("(Click to open Discord)")))
|
||||
.clickEvent(net.kyori.adventure.text.event.ClickEvent.clickEvent(net.kyori.adventure.text.event.ClickEvent.Action.OPEN_URL, "https://jonesdev.xyz/discord/")),
|
||||
Component.text("or open a new issue on GitHub.", NamedTextColor.YELLOW)
|
||||
.hoverEvent(net.kyori.adventure.text.event.HoverEvent.hoverEvent(net.kyori.adventure.text.event.HoverEvent.Action.SHOW_TEXT, Component.text("(Click to open GitHub)")))
|
||||
.clickEvent(net.kyori.adventure.text.event.ClickEvent.clickEvent(net.kyori.adventure.text.event.ClickEvent.Action.OPEN_URL, "https://github.com/jonesdevelopment/sonar" +
|
||||
"/issues"))
|
||||
),
|
||||
Component.empty()
|
||||
));
|
||||
|
||||
Sonar.get().getSubcommandRegistry().getSubcommands().forEach(sub -> {
|
||||
Component component = Component.textOfChildren(
|
||||
Component.text(" ▪ ", NamedTextColor.GRAY),
|
||||
Component.text("/sonar " + sub.getInfo().name(), NamedTextColor.GREEN),
|
||||
Component.text(" - ", NamedTextColor.GRAY),
|
||||
Component.text(sub.getInfo().description(), NamedTextColor.WHITE)
|
||||
);
|
||||
|
||||
component = component.clickEvent(
|
||||
net.kyori.adventure.text.event.ClickEvent.clickEvent(ClickEvent.Action.SUGGEST_COMMAND,
|
||||
"/sonar " + sub.getInfo().name() + " ")
|
||||
).hoverEvent(
|
||||
net.kyori.adventure.text.event.HoverEvent.hoverEvent(HoverEvent.Action.SHOW_TEXT, Component.text(
|
||||
"§7Only players: §f" + (sub.getInfo().onlyPlayers() ? "§a✔" : "§c✗")
|
||||
+ Sonar.LINE_SEPARATOR + "§7Require console: §f" + (sub.getInfo().onlyConsole() ? "§a✔" : "§c✗")
|
||||
+ Sonar.LINE_SEPARATOR + "§7Permission: §f" + sub.getPermission()
|
||||
+ Sonar.LINE_SEPARATOR + "§7Aliases: §f" + sub.getAliases()
|
||||
))
|
||||
);
|
||||
CACHED_HELP_MESSAGE.add(component);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.command.CommandInvocation;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSender;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.argument.Argument;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -49,11 +49,9 @@ public abstract class Subcommand {
|
||||
.collect(Collectors.joining(", "));
|
||||
}
|
||||
|
||||
protected final void incorrectUsage(final @NotNull InvocationSender sender) {
|
||||
sender.sendMessage(
|
||||
SONAR.getConfig().INCORRECT_COMMAND_USAGE
|
||||
.replace("%usage%", info.name() + " (" + arguments + ")")
|
||||
);
|
||||
protected final void incorrectUsage(final @NotNull InvocationSource sender) {
|
||||
sender.sendMessage(SONAR.getConfig().getIncorrectCommandUsage()
|
||||
.replace("%usage%", info.name() + " (" + arguments + ")"));
|
||||
}
|
||||
|
||||
public abstract void execute(final @NotNull CommandInvocation invocation);
|
||||
|
||||
@@ -20,13 +20,10 @@ package xyz.jonesdev.sonar.api.config;
|
||||
import lombok.Getter;
|
||||
import org.simpleyaml.configuration.comments.format.YamlCommentFormat;
|
||||
import org.simpleyaml.configuration.file.YamlFile;
|
||||
import org.simpleyaml.exceptions.InvalidConfigurationException;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -66,44 +63,20 @@ public final class SimpleYamlConfig {
|
||||
}
|
||||
}
|
||||
|
||||
public void load() {
|
||||
String backupContent = null;
|
||||
try {
|
||||
// https://github.com/jonesdevelopment/sonar/issues/26
|
||||
// Only load the configuration if the file already exists
|
||||
if (yaml.exists()) {
|
||||
try {
|
||||
// Load all lines from the existing files before trying to load/overwrite it
|
||||
backupContent = String.join(LINE_SEPARATOR, Files.readAllLines(file.toPath()));
|
||||
} catch (IOException exception) {
|
||||
Sonar.get().getLogger().error("Error while creating backup configuration: {}", exception);
|
||||
}
|
||||
yaml.loadWithComments();
|
||||
} else {
|
||||
yaml.createOrLoadWithComments();
|
||||
}
|
||||
|
||||
// Always load the comment format and options
|
||||
yaml.setCommentFormat(YamlCommentFormat.PRETTY);
|
||||
|
||||
yaml.options().headerFormatter().commentPrefix("# ");
|
||||
yaml.setHeader(String.join(LINE_SEPARATOR, HEADER));
|
||||
} catch (InvalidConfigurationException exception) {
|
||||
Sonar.get().getLogger().error("Invalid configuration: {}", exception);
|
||||
|
||||
// Try to save a backup configuration to prevent data loss
|
||||
if (backupContent != null) {
|
||||
final File backupFile = new File(file.getParentFile(), file.getName() + ".broken");
|
||||
try (final FileWriter fileWriter = new FileWriter(backupFile, false)) {
|
||||
fileWriter.write(backupContent);
|
||||
Sonar.get().getLogger().info("Saved backup configuration to {}", backupFile.getName());
|
||||
} catch (Throwable throwable) {
|
||||
Sonar.get().getLogger().warn("Could not load from backup configuration: {}", throwable);
|
||||
}
|
||||
}
|
||||
} catch (IOException exception) {
|
||||
Sonar.get().getLogger().error("Error while loading configuration: {}", exception);
|
||||
public void load() throws Exception {
|
||||
// https://github.com/jonesdevelopment/sonar/issues/26
|
||||
// Only load the configuration if the file already exists
|
||||
if (yaml.exists()) {
|
||||
yaml.loadWithComments();
|
||||
} else {
|
||||
yaml.createOrLoadWithComments();
|
||||
}
|
||||
|
||||
// Always load the comment format and options
|
||||
yaml.setCommentFormat(YamlCommentFormat.PRETTY);
|
||||
|
||||
yaml.options().headerFormatter().commentPrefix("# ");
|
||||
yaml.setHeader(String.join(LINE_SEPARATOR, HEADER));
|
||||
}
|
||||
|
||||
public void set(final String path, final Object v) {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+1
-3
@@ -48,10 +48,8 @@ public final class VerifiedPlayerController {
|
||||
private final @NotNull SonarConfiguration.DatabaseType cachedDatabaseType;
|
||||
|
||||
public VerifiedPlayerController() {
|
||||
final SonarConfiguration config = Sonar.get().getConfig();
|
||||
|
||||
// Cache selected database type, so we don't need to call Sonar.get() every time
|
||||
cachedDatabaseType = config.DATABASE_TYPE;
|
||||
cachedDatabaseType = Sonar.get().getConfig().getDatabaseType();
|
||||
|
||||
// Don't establish a database connection if the type is NONE
|
||||
if (cachedDatabaseType == SonarConfiguration.DatabaseType.NONE) {
|
||||
|
||||
@@ -38,8 +38,8 @@ public class DependencyLoader {
|
||||
public ConnectionSource setUpDriverAndConnect() throws Throwable {
|
||||
final SonarConfiguration config = Sonar.get().getConfig();
|
||||
|
||||
final URL[] urls = new URL[config.DATABASE_TYPE.getDependencies().length];
|
||||
for (final Dependency dependency : config.DATABASE_TYPE.getDependencies()) {
|
||||
final URL[] urls = new URL[config.getDatabaseType().getDependencies().length];
|
||||
for (final Dependency dependency : config.getDatabaseType().getDependencies()) {
|
||||
final URL url = dependency.getClassLoaderURL();
|
||||
final ClassLoader currentClassLoader = DependencyLoader.class.getClassLoader();
|
||||
|
||||
@@ -50,9 +50,9 @@ public class DependencyLoader {
|
||||
urls[dependency.ordinal()] = url;
|
||||
}
|
||||
|
||||
final String type = config.DATABASE_TYPE.name().toLowerCase();
|
||||
final String type = config.getDatabaseType().name().toLowerCase();
|
||||
final String databaseURL =
|
||||
"jdbc:" + type + "://" + config.SQL_URL + ":" + config.SQL_PORT + "/" + config.SQL_DATABASE;
|
||||
"jdbc:" + type + "://" + config.getSqlUrl() + ":" + config.getSqlPort() + "/" + config.getSqlDatabase();
|
||||
|
||||
final ExternalClassLoader classLoader = new ExternalClassLoader(urls);
|
||||
final Connection connection = connect(classLoader, databaseURL, config);
|
||||
@@ -64,17 +64,17 @@ public class DependencyLoader {
|
||||
private Connection connect(final @NotNull ClassLoader classLoader,
|
||||
final @NotNull String databaseURL,
|
||||
final @NotNull SonarConfiguration configuration) throws Throwable {
|
||||
final Class<?> driverClass = classLoader.loadClass(configuration.DATABASE_TYPE.getDriverClassName());
|
||||
final Class<?> driverClass = classLoader.loadClass(configuration.getDatabaseType().getDriverClassName());
|
||||
final Object driver = driverClass.getDeclaredConstructor().newInstance();
|
||||
|
||||
DriverManager.registerDriver((Driver) driver);
|
||||
|
||||
final Properties properties = new Properties();
|
||||
if (!configuration.SQL_USER.isEmpty()) {
|
||||
properties.put("user", configuration.SQL_USER);
|
||||
if (!configuration.getSqlUser().isEmpty()) {
|
||||
properties.put("user", configuration.getSqlUser());
|
||||
}
|
||||
if (!configuration.SQL_PASSWORD.isEmpty()) {
|
||||
properties.put("password", configuration.SQL_PASSWORD);
|
||||
if (!configuration.getSqlPassword().isEmpty()) {
|
||||
properties.put("password", configuration.getSqlPassword());
|
||||
}
|
||||
|
||||
final Method connect = driverClass.getDeclaredMethod("connect", String.class, Properties.class);
|
||||
|
||||
@@ -65,7 +65,7 @@ public final class Fallback {
|
||||
};
|
||||
|
||||
public boolean isPotentiallyUnderAttack() {
|
||||
return connected.size() > SONAR.getConfig().MINIMUM_PLAYERS_FOR_ATTACK
|
||||
|| queue.getQueuedPlayers().size() > SONAR.getConfig().MINIMUM_PLAYERS_FOR_ATTACK;
|
||||
return connected.size() > SONAR.getConfig().getMinimumPlayersForAttack()
|
||||
|| queue.getQueuedPlayers().size() > SONAR.getConfig().getMinimumPlayersForAttack();
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ public final class FallbackQueue {
|
||||
}
|
||||
|
||||
public void poll() {
|
||||
final int max = Sonar.get().getConfig().MAXIMUM_QUEUE_POLLS;
|
||||
final int max = Sonar.get().getConfig().getMaximumQueuePolls();
|
||||
int index = 0;
|
||||
|
||||
// We need to be very careful here since we don't want any concurrency issues.
|
||||
|
||||
@@ -19,6 +19,7 @@ package xyz.jonesdev.sonar.api.fallback;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import xyz.jonesdev.cappuccino.Cappuccino;
|
||||
@@ -51,9 +52,20 @@ public interface FallbackUser<X, Y> {
|
||||
* Kicks the player from the server with
|
||||
* the given disconnect message.
|
||||
*
|
||||
* @param reason Disconnect message
|
||||
* @param reason Legacy disconnect message string
|
||||
* @see #disconnect(Component)
|
||||
*/
|
||||
void disconnect(final @NotNull String reason);
|
||||
default void disconnect(final @NotNull String reason) {
|
||||
disconnect(Component.text(reason));
|
||||
}
|
||||
|
||||
/**
|
||||
* Kicks the player from the server with
|
||||
* the given disconnect message.
|
||||
*
|
||||
* @param reason Disconnect message component
|
||||
*/
|
||||
void disconnect(final @NotNull Component reason);
|
||||
|
||||
/**
|
||||
* Sends a packet/message to the player
|
||||
@@ -77,10 +89,10 @@ public interface FallbackUser<X, Y> {
|
||||
// Call the VerifyFailedEvent for external API usage
|
||||
Sonar.get().getEventManager().publish(new UserVerifyFailedEvent(this, reason));
|
||||
|
||||
disconnect(Sonar.get().getConfig().VERIFICATION_FAILED);
|
||||
disconnect(Sonar.get().getConfig().getVerificationFailed());
|
||||
|
||||
if (reason != null) {
|
||||
getFallback().getLogger().info(Sonar.get().getConfig().VERIFICATION_FAILED_LOG
|
||||
getFallback().getLogger().info(Sonar.get().getConfig().getVerificationFailedLog()
|
||||
.replace("%ip%", Sonar.get().getConfig().formatAddress(getInetAddress()))
|
||||
.replace("%protocol%", String.valueOf(getProtocolVersion().getProtocol()))
|
||||
.replace("%reason%", reason));
|
||||
@@ -98,7 +110,7 @@ public interface FallbackUser<X, Y> {
|
||||
Sonar.get().getEventManager().publish(new UserBlacklistedEvent(this));
|
||||
|
||||
getFallback().getBlacklisted().put(getInetAddress().toString());
|
||||
getFallback().getLogger().info(Sonar.get().getConfig().VERIFICATION_BLACKLIST_LOG
|
||||
getFallback().getLogger().info(Sonar.get().getConfig().getVerificationBlacklistLog()
|
||||
.replace("%ip%", Sonar.get().getConfig().formatAddress(getInetAddress()))
|
||||
.replace("%protocol%", String.valueOf(getProtocolVersion().getProtocol())));
|
||||
} else {
|
||||
|
||||
@@ -17,22 +17,26 @@
|
||||
|
||||
package xyz.jonesdev.sonar.api.server;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import xyz.jonesdev.sonar.api.SonarPlatform;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSender;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface ServerWrapper {
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public abstract class ServerWrapper {
|
||||
|
||||
/**
|
||||
* @return Platform of the server (Velocity, BungeeCord, Spigot)
|
||||
* Platform of the server (Velocity, BungeeCord, Spigot)
|
||||
*/
|
||||
SonarPlatform getPlatform();
|
||||
private final SonarPlatform platform;
|
||||
|
||||
/**
|
||||
* @param username Username of the player
|
||||
* @return Optional player wrapped as InvocationSender
|
||||
* @see xyz.jonesdev.sonar.api.command.InvocationSender
|
||||
* @see InvocationSource
|
||||
*/
|
||||
Optional<InvocationSender> getOnlinePlayer(final String username);
|
||||
public abstract Optional<InvocationSource> getOnlinePlayer(final String username);
|
||||
}
|
||||
@@ -57,8 +57,8 @@ public abstract class Verbose implements JVMProfiler {
|
||||
broadcast(prepareActionBarFormat());
|
||||
}
|
||||
|
||||
protected final @NotNull String prepareActionBarFormat() {
|
||||
return Sonar.get().getConfig().ACTION_BAR_LAYOUT
|
||||
protected @NotNull String prepareActionBarFormat() {
|
||||
return Sonar.get().getConfig().getActionBarLayout()
|
||||
.replace("%queued%",
|
||||
DECIMAL_FORMAT.format(Sonar.get().getFallback().getQueue().getQueuedPlayers().size()))
|
||||
.replace("%verifying%", DECIMAL_FORMAT.format(Sonar.get().getFallback().getConnected().size()))
|
||||
@@ -82,33 +82,35 @@ public abstract class Verbose implements JVMProfiler {
|
||||
}
|
||||
|
||||
protected final String nextAnimation() {
|
||||
val animations = Sonar.get().getConfig().ANIMATION;
|
||||
val animations = Sonar.get().getConfig().getAnimation();
|
||||
final int nextIndex = ++animationIndex % animations.size();
|
||||
return animations.toArray(new String[0])[nextIndex];
|
||||
}
|
||||
|
||||
// Run action bar verbose
|
||||
protected abstract void broadcast(final String message);
|
||||
protected void broadcast(final String message) {
|
||||
// This should be replaced by the wrapper
|
||||
}
|
||||
|
||||
/**
|
||||
* @param subscriber Name of the player who subscribed
|
||||
* @return Whether the player is subscribed or not
|
||||
*/
|
||||
public boolean isSubscribed(final @NotNull String subscriber) {
|
||||
public final boolean isSubscribed(final @NotNull String subscriber) {
|
||||
return subscribers.contains(subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param subscriber Name of the player to subscribe
|
||||
*/
|
||||
public void subscribe(final @NotNull String subscriber) {
|
||||
public final void subscribe(final @NotNull String subscriber) {
|
||||
subscribers.add(subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param subscriber Name of the player to unsubscribe
|
||||
*/
|
||||
public void unsubscribe(final @NotNull String subscriber) {
|
||||
public final void unsubscribe(final @NotNull String subscriber) {
|
||||
subscribers.remove(subscriber);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ bukkit {
|
||||
|
||||
commands {
|
||||
register("sonar") {
|
||||
permission = "sonar.command"
|
||||
// No permission check here
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,9 @@ dependencies {
|
||||
compileOnly(project(":api"))
|
||||
compileOnly(project(":common"))
|
||||
|
||||
// MiniMessage platform support
|
||||
implementation("net.kyori:adventure-platform-bukkit:4.3.1")
|
||||
|
||||
// We have to use 1.8 for backwards compatibility
|
||||
compileOnly("org.spigotmc:spigot-api:1.8.8-R0.1-SNAPSHOT")
|
||||
|
||||
|
||||
@@ -21,11 +21,12 @@ import lombok.Getter;
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.SonarPlatform;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSender;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.api.fallback.traffic.TrafficCounter;
|
||||
import xyz.jonesdev.sonar.api.logger.LoggerWrapper;
|
||||
import xyz.jonesdev.sonar.api.server.ServerWrapper;
|
||||
import xyz.jonesdev.sonar.bukkit.command.SonarCommand;
|
||||
import xyz.jonesdev.sonar.bukkit.command.BukkitInvocationSource;
|
||||
import xyz.jonesdev.sonar.bukkit.command.BukkitSonarCommand;
|
||||
import xyz.jonesdev.sonar.bukkit.verbose.ActionBarVerbose;
|
||||
import xyz.jonesdev.sonar.common.boot.SonarBootstrap;
|
||||
|
||||
@@ -72,44 +73,25 @@ public final class SonarBukkit extends SonarBootstrap<SonarBukkitPlugin> {
|
||||
* <br>
|
||||
* We have to do this, so we can access all necessary API functions.
|
||||
*/
|
||||
public final ServerWrapper server = new ServerWrapper() {
|
||||
public final ServerWrapper server = new ServerWrapper(SonarPlatform.BUKKIT) {
|
||||
|
||||
@Override
|
||||
public SonarPlatform getPlatform() {
|
||||
return SonarPlatform.VELOCITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<InvocationSender> getOnlinePlayer(final String username) {
|
||||
public Optional<InvocationSource> getOnlinePlayer(final String username) {
|
||||
return getPlugin().getServer().getOnlinePlayers().stream()
|
||||
.filter(player -> player.getName().equalsIgnoreCase(username))
|
||||
.findFirst()
|
||||
.map(player -> new InvocationSender() {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return player.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(final String message) {
|
||||
player.sendMessage(message);
|
||||
}
|
||||
});
|
||||
.map(BukkitInvocationSource::new);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
|
||||
// Reload configuration
|
||||
reload();
|
||||
|
||||
// Initialize bStats.org metrics
|
||||
new Metrics(getPlugin(), getServer().getPlatform().getMetricsId());
|
||||
|
||||
// Register Sonar command
|
||||
Objects.requireNonNull(getPlugin().getCommand("sonar")).setExecutor(new SonarCommand());
|
||||
Objects.requireNonNull(getPlugin().getCommand("sonar")).setExecutor(new BukkitSonarCommand());
|
||||
|
||||
// Register Fallback queue task
|
||||
getPlugin().getServer().getScheduler().runTaskTimerAsynchronously(getPlugin(), getFallback().getQueue()::poll,
|
||||
|
||||
+11
-10
@@ -15,17 +15,18 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.api.command;
|
||||
package xyz.jonesdev.sonar.bukkit.command;
|
||||
|
||||
import xyz.jonesdev.cappuccino.Cappuccino;
|
||||
import xyz.jonesdev.cappuccino.ExpiringCache;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.bukkit.SonarBukkit;
|
||||
|
||||
import java.util.*;
|
||||
public final class BukkitInvocationSource extends InvocationSource {
|
||||
private static final BukkitAudiences AUDIENCES = BukkitAudiences.create(SonarBukkit.INSTANCE.getPlugin());
|
||||
|
||||
public interface SonarBaseCommand {
|
||||
List<String> TAB_SUGGESTIONS = new ArrayList<>();
|
||||
Map<String, List<String>> ARG_TAB_SUGGESTIONS = new HashMap<>();
|
||||
ExpiringCache<Object> DELAY = Cappuccino.buildExpiring(500L);
|
||||
List<Object> CACHED_HELP_MESSAGE = new Vector<>();
|
||||
int COPYRIGHT_YEAR = Calendar.getInstance().get(Calendar.YEAR);
|
||||
public BukkitInvocationSource(final @NotNull CommandSender sender) {
|
||||
super(sender.getName(), AUDIENCES.sender(sender));
|
||||
}
|
||||
}
|
||||
+27
-94
@@ -17,18 +17,14 @@
|
||||
|
||||
package xyz.jonesdev.sonar.bukkit.command;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||
import net.md_5.bungee.api.chat.HoverEvent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.command.*;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.command.CommandInvocation;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSender;
|
||||
import xyz.jonesdev.sonar.api.command.SonarBaseCommand;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.api.command.SonarCommand;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.Subcommand;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.argument.Argument;
|
||||
|
||||
@@ -39,62 +35,9 @@ import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
public final class SonarCommand implements CommandExecutor, TabExecutor, SonarBaseCommand {
|
||||
private static final TextComponent GITHUB_LINK_COMPONENT = new TextComponent(ChatColor.GREEN + "https://github" +
|
||||
".com/jonesdevelopment/sonar");
|
||||
private static final TextComponent DISCORD_SUPPORT = new TextComponent(ChatColor.YELLOW + "Open a ticket on the " +
|
||||
"Discord ");
|
||||
private static final TextComponent GITHUB_ISSUES = new TextComponent(ChatColor.YELLOW + "or open a new issue on " +
|
||||
"GitHub.");
|
||||
|
||||
static {
|
||||
GITHUB_LINK_COMPONENT.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://github" +
|
||||
".com/jonesdevelopment/sonar"));
|
||||
DISCORD_SUPPORT.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://jonesdev.xyz/discord/"));
|
||||
DISCORD_SUPPORT.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("(Click to open " +
|
||||
"Discord)").create()));
|
||||
GITHUB_ISSUES.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://github.com/jonesdevelopment/sonar" +
|
||||
"/issues"));
|
||||
GITHUB_ISSUES.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("(Click to open " +
|
||||
"GitHub)").create()));
|
||||
}
|
||||
|
||||
static {
|
||||
CACHED_HELP_MESSAGE.addAll(Arrays.asList(
|
||||
new TextComponent(ChatColor.YELLOW + "Running Sonar " + Sonar.get().getVersion()
|
||||
+ " on " + Sonar.get().getServer().getPlatform().getDisplayName()
|
||||
+ "."),
|
||||
new TextComponent(ChatColor.YELLOW + "(C) " + COPYRIGHT_YEAR + " Jones Development and Sonar Contributors"),
|
||||
GITHUB_LINK_COMPONENT,
|
||||
new TextComponent(""),
|
||||
new TextComponent(ChatColor.YELLOW + "Need help or have any questions?"),
|
||||
new TextComponent(
|
||||
DISCORD_SUPPORT,
|
||||
GITHUB_ISSUES
|
||||
),
|
||||
new TextComponent("")
|
||||
));
|
||||
|
||||
Sonar.get().getSubcommandRegistry().getSubcommands().forEach(sub -> {
|
||||
final TextComponent component = new TextComponent(
|
||||
new TextComponent(ChatColor.GRAY + " ▪ "),
|
||||
new TextComponent(ChatColor.GREEN + "/sonar " + sub.getInfo().name()),
|
||||
new TextComponent(ChatColor.GRAY + " - "),
|
||||
new TextComponent(ChatColor.WHITE + sub.getInfo().description())
|
||||
);
|
||||
|
||||
component.setClickEvent(
|
||||
new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/sonar " + sub.getInfo().name() + " ")
|
||||
);
|
||||
component.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(
|
||||
"§7Only players: §f" + (sub.getInfo().onlyPlayers() ? "§a✔" : "§c✗")
|
||||
+ Sonar.LINE_SEPARATOR + "§7Require console: §f" + (sub.getInfo().onlyConsole() ? "§a✔" : "§c✗")
|
||||
+ Sonar.LINE_SEPARATOR + "§7Permission: §f" + sub.getPermission()
|
||||
+ Sonar.LINE_SEPARATOR + "§7Aliases: §f" + sub.getAliases()
|
||||
).create())
|
||||
);
|
||||
CACHED_HELP_MESSAGE.add(component);
|
||||
});
|
||||
public final class BukkitSonarCommand implements CommandExecutor, TabExecutor, SonarCommand {
|
||||
{
|
||||
cacheHelpMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -103,6 +46,11 @@ public final class SonarCommand implements CommandExecutor, TabExecutor, SonarBa
|
||||
final String label,
|
||||
final String[] args) {
|
||||
if (!(sender instanceof ConsoleCommandSender)) {
|
||||
// Check if the player actually has the permission to run the command
|
||||
if (!sender.hasPermission("sonar.command")) {
|
||||
sender.sendMessage(Sonar.get().getConfig().getNoPermission());
|
||||
return false;
|
||||
}
|
||||
// Checking if it contains will only break more since it can throw
|
||||
// a NullPointerException if the cache is being accessed from parallel threads
|
||||
DELAY.cleanUp(); // Clean up the cache
|
||||
@@ -112,14 +60,14 @@ public final class SonarCommand implements CommandExecutor, TabExecutor, SonarBa
|
||||
// Spamming should be prevented, especially if some heavy operations are done,
|
||||
// which is not the case here but let's still stay safe!
|
||||
if (mapTimestamp > 0L) {
|
||||
sender.sendMessage(Sonar.get().getConfig().COMMAND_COOL_DOWN);
|
||||
sender.sendMessage(Sonar.get().getConfig().getCommandCoolDown());
|
||||
|
||||
// Format delay
|
||||
final long timestamp = System.currentTimeMillis();
|
||||
final double left = 0.5D - (timestamp - mapTimestamp) / 1000D;
|
||||
|
||||
sender.sendMessage(
|
||||
Sonar.get().getConfig().COMMAND_COOL_DOWN_LEFT
|
||||
Sonar.get().getConfig().getCommandCoolDownLeft()
|
||||
.replace("%time-left%", Sonar.DECIMAL_FORMAT.format(left))
|
||||
);
|
||||
return false;
|
||||
@@ -130,18 +78,7 @@ public final class SonarCommand implements CommandExecutor, TabExecutor, SonarBa
|
||||
|
||||
Optional<Subcommand> subcommand = Optional.empty();
|
||||
|
||||
final InvocationSender invocationSender = new InvocationSender() {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return sender.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(final String message) {
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
};
|
||||
final InvocationSource invocationSource = new BukkitInvocationSource(sender);
|
||||
|
||||
if (args.length > 0) {
|
||||
// Search subcommand if command arguments are present
|
||||
@@ -156,10 +93,8 @@ public final class SonarCommand implements CommandExecutor, TabExecutor, SonarBa
|
||||
if (!subcommand.get().getInfo().onlyConsole()
|
||||
&& !sender.hasPermission(subcommand.get().getPermission())
|
||||
) {
|
||||
invocationSender.sendMessage(
|
||||
Sonar.get().getConfig().SUB_COMMAND_NO_PERM
|
||||
.replace("%permission%", subcommand.get().getPermission())
|
||||
);
|
||||
invocationSource.sendMessage(Sonar.get().getConfig().getSubCommandNoPerm()
|
||||
.replace("%permission%", subcommand.get().getPermission()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -167,24 +102,22 @@ public final class SonarCommand implements CommandExecutor, TabExecutor, SonarBa
|
||||
|
||||
subcommand.ifPresent(sub -> {
|
||||
if (sub.getInfo().onlyPlayers() && !(sender instanceof Player)) {
|
||||
invocationSender.sendMessage(Sonar.get().getConfig().PLAYERS_ONLY);
|
||||
invocationSource.sendMessage(Sonar.get().getConfig().getPlayersOnly());
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub.getInfo().onlyConsole() && !(sender instanceof ConsoleCommandSender)) {
|
||||
invocationSender.sendMessage(Sonar.get().getConfig().CONSOLE_ONLY);
|
||||
invocationSource.sendMessage(Sonar.get().getConfig().getConsoleOnly());
|
||||
return;
|
||||
}
|
||||
|
||||
final CommandInvocation commandInvocation = new CommandInvocation(invocationSender, sub, args);
|
||||
final CommandInvocation commandInvocation = new CommandInvocation(invocationSource, sub, args);
|
||||
|
||||
// The subcommands has arguments which are not present in the executed command
|
||||
if (sub.getInfo().arguments().length > 0
|
||||
&& commandInvocation.getRawArguments().length <= 1) {
|
||||
invocationSender.sendMessage(
|
||||
Sonar.get().getConfig().INCORRECT_COMMAND_USAGE
|
||||
.replace("%usage%", sub.getInfo().name() + " (" + sub.getArguments() + ")")
|
||||
);
|
||||
invocationSource.sendMessage(Sonar.get().getConfig().getIncorrectCommandUsage()
|
||||
.replace("%usage%", sub.getInfo().name() + " (" + sub.getArguments() + ")"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -197,12 +130,8 @@ public final class SonarCommand implements CommandExecutor, TabExecutor, SonarBa
|
||||
// Re-use the old, cached help message since we don't want to scan
|
||||
// for each subcommand and it's arguments/attributes every time
|
||||
// someone runs /sonar since the subcommand don't change
|
||||
for (final Object component : CACHED_HELP_MESSAGE) {
|
||||
if (sender instanceof Player) {
|
||||
((Player) sender).spigot().sendMessage((TextComponent) component);
|
||||
} else {
|
||||
sender.sendMessage(((TextComponent) component).getText());
|
||||
}
|
||||
for (final Component component : CACHED_HELP_MESSAGE) {
|
||||
invocationSource.sendMessage(component);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -213,6 +142,10 @@ public final class SonarCommand implements CommandExecutor, TabExecutor, SonarBa
|
||||
final Command command,
|
||||
final String commandAlias,
|
||||
final String @NotNull [] args) {
|
||||
// Do not allow tab completion if the player does not have the required permission
|
||||
if (!sender.hasPermission("sonar.command")) {
|
||||
return emptyList();
|
||||
}
|
||||
if (args.length <= 1) {
|
||||
if (TAB_SUGGESTIONS.isEmpty()) {
|
||||
for (final Subcommand subcommand : Sonar.get().getSubcommandRegistry().getSubcommands()) {
|
||||
@@ -7,7 +7,7 @@ apply(plugin = "net.minecrell.plugin-yml.bungee")
|
||||
bungee {
|
||||
name = rootProject.name
|
||||
version = rootProject.version.toString().split("-")[0]
|
||||
main = "xyz.jonesdev.sonar.bukkit.SonarBukkitPlugin"
|
||||
main = "xyz.jonesdev.sonar.bungee.SonarBungeePlugin"
|
||||
author = "Jones Development, Sonar Contributors"
|
||||
description = "Effective Anti-bot plugin for Velocity, BungeeCord, and Bukkit (1.7-latest)"
|
||||
}
|
||||
@@ -24,6 +24,9 @@ dependencies {
|
||||
|
||||
compileOnly("net.md_5:bungeecord:1.20.2-rc2-SNAPSHOT")
|
||||
|
||||
// MiniMessage platform support
|
||||
implementation("net.kyori:adventure-platform-bungeecord:4.3.1")
|
||||
|
||||
// Implement bStats.org for metrics
|
||||
implementation("org.bstats:bstats-bungeecord:3.0.2")
|
||||
}
|
||||
|
||||
@@ -18,15 +18,15 @@
|
||||
package xyz.jonesdev.sonar.bungee;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.bstats.bungeecord.Metrics;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.SonarPlatform;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSender;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.api.fallback.traffic.TrafficCounter;
|
||||
import xyz.jonesdev.sonar.api.logger.LoggerWrapper;
|
||||
import xyz.jonesdev.sonar.api.server.ServerWrapper;
|
||||
import xyz.jonesdev.sonar.bungee.command.SonarCommand;
|
||||
import xyz.jonesdev.sonar.bungee.command.BungeeInvocationSource;
|
||||
import xyz.jonesdev.sonar.bungee.command.BungeeSonarCommand;
|
||||
import xyz.jonesdev.sonar.bungee.fallback.FallbackListener;
|
||||
import xyz.jonesdev.sonar.bungee.fallback.injection.BaseInjectionHelper;
|
||||
import xyz.jonesdev.sonar.bungee.fallback.injection.ChildChannelInitializer;
|
||||
@@ -76,44 +76,25 @@ public final class SonarBungee extends SonarBootstrap<SonarBungeePlugin> {
|
||||
* <br>
|
||||
* We have to do this, so we can access all necessary API functions.
|
||||
*/
|
||||
public final ServerWrapper server = new ServerWrapper() {
|
||||
public final ServerWrapper server = new ServerWrapper(SonarPlatform.BUNGEE) {
|
||||
|
||||
@Override
|
||||
public SonarPlatform getPlatform() {
|
||||
return SonarPlatform.BUNGEE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<InvocationSender> getOnlinePlayer(final String username) {
|
||||
public Optional<InvocationSource> getOnlinePlayer(final String username) {
|
||||
return getPlugin().getServer().getPlayers().stream()
|
||||
.filter(player -> player.getName().equalsIgnoreCase(username))
|
||||
.findFirst()
|
||||
.map(player -> new InvocationSender() {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return player.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(final String message) {
|
||||
player.sendMessage(new TextComponent(message));
|
||||
}
|
||||
});
|
||||
.map(BungeeInvocationSource::new);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
|
||||
// Reload configuration
|
||||
reload();
|
||||
|
||||
// Initialize bStats.org metrics
|
||||
new Metrics(getPlugin(), getServer().getPlatform().getMetricsId());
|
||||
|
||||
// Register Sonar command
|
||||
getPlugin().getServer().getPluginManager().registerCommand(getPlugin(), new SonarCommand());
|
||||
getPlugin().getServer().getPluginManager().registerCommand(getPlugin(), new BungeeSonarCommand());
|
||||
|
||||
// Register Fallback listener
|
||||
getPlugin().getServer().getPluginManager().registerListener(getPlugin(), new FallbackListener(getFallback()));
|
||||
@@ -136,8 +117,5 @@ public final class SonarBungee extends SonarBootstrap<SonarBungeePlugin> {
|
||||
|
||||
@Override
|
||||
public void postReload() {
|
||||
|
||||
// Prepare cached messages
|
||||
FallbackListener.CachedMessages.update();
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Sonar Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.bungee.command;
|
||||
|
||||
import net.kyori.adventure.platform.bungeecord.BungeeAudiences;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.bungee.SonarBungee;
|
||||
|
||||
public final class BungeeInvocationSource extends InvocationSource {
|
||||
private static final BungeeAudiences AUDIENCES = BungeeAudiences.create(SonarBungee.INSTANCE.getPlugin());
|
||||
|
||||
public BungeeInvocationSource(final @NotNull CommandSender sender) {
|
||||
super(sender.getName(), AUDIENCES.sender(sender));
|
||||
}
|
||||
}
|
||||
+29
-94
@@ -17,11 +17,8 @@
|
||||
|
||||
package xyz.jonesdev.sonar.bungee.command;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||
import net.md_5.bungee.api.chat.HoverEvent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Command;
|
||||
@@ -30,8 +27,8 @@ import net.md_5.bungee.command.ConsoleCommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.command.CommandInvocation;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSender;
|
||||
import xyz.jonesdev.sonar.api.command.SonarBaseCommand;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.api.command.SonarCommand;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.Subcommand;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.argument.Argument;
|
||||
|
||||
@@ -42,72 +39,21 @@ import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
public final class SonarCommand extends Command implements TabExecutor, SonarBaseCommand {
|
||||
public SonarCommand() {
|
||||
super("sonar", "sonar.command");
|
||||
}
|
||||
|
||||
private static final TextComponent GITHUB_LINK_COMPONENT = new TextComponent(ChatColor.GREEN + "https://github" +
|
||||
".com/jonesdevelopment/sonar");
|
||||
private static final TextComponent DISCORD_SUPPORT = new TextComponent(ChatColor.YELLOW + "Open a ticket on the " +
|
||||
"Discord ");
|
||||
private static final TextComponent GITHUB_ISSUES = new TextComponent(ChatColor.YELLOW + "or open a new issue on " +
|
||||
"GitHub.");
|
||||
|
||||
static {
|
||||
GITHUB_LINK_COMPONENT.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://github" +
|
||||
".com/jonesdevelopment/sonar"));
|
||||
DISCORD_SUPPORT.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://jonesdev.xyz/discord/"));
|
||||
DISCORD_SUPPORT.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("(Click to open " +
|
||||
"Discord)").create()));
|
||||
GITHUB_ISSUES.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://github.com/jonesdevelopment/sonar" +
|
||||
"/issues"));
|
||||
GITHUB_ISSUES.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("(Click to open " +
|
||||
"GitHub)").create()));
|
||||
}
|
||||
|
||||
static {
|
||||
CACHED_HELP_MESSAGE.addAll(Arrays.asList(
|
||||
new TextComponent(ChatColor.YELLOW + "Running Sonar " + Sonar.get().getVersion()
|
||||
+ " on " + Sonar.get().getServer().getPlatform().getDisplayName()
|
||||
+ "."),
|
||||
new TextComponent(ChatColor.YELLOW + "(C) " + COPYRIGHT_YEAR + " Jones Development and Sonar Contributors"),
|
||||
GITHUB_LINK_COMPONENT,
|
||||
new TextComponent(""),
|
||||
new TextComponent(ChatColor.YELLOW + "Need help or have any questions?"),
|
||||
new TextComponent(
|
||||
DISCORD_SUPPORT,
|
||||
GITHUB_ISSUES
|
||||
),
|
||||
new TextComponent("")
|
||||
));
|
||||
|
||||
Sonar.get().getSubcommandRegistry().getSubcommands().forEach(sub -> {
|
||||
final TextComponent component = new TextComponent(
|
||||
new TextComponent(ChatColor.GRAY + " ▪ "),
|
||||
new TextComponent(ChatColor.GREEN + "/sonar " + sub.getInfo().name()),
|
||||
new TextComponent(ChatColor.GRAY + " - "),
|
||||
new TextComponent(ChatColor.WHITE + sub.getInfo().description())
|
||||
);
|
||||
|
||||
component.setClickEvent(
|
||||
new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/sonar " + sub.getInfo().name() + " ")
|
||||
);
|
||||
component.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(
|
||||
"§7Only players: §f" + (sub.getInfo().onlyPlayers() ? "§a✔" : "§c✗")
|
||||
+ Sonar.LINE_SEPARATOR + "§7Require console: §f" + (sub.getInfo().onlyConsole() ? "§a✔" : "§c✗")
|
||||
+ Sonar.LINE_SEPARATOR + "§7Permission: §f" + sub.getPermission()
|
||||
+ Sonar.LINE_SEPARATOR + "§7Aliases: §f" + sub.getAliases()
|
||||
).create())
|
||||
);
|
||||
CACHED_HELP_MESSAGE.add(component);
|
||||
});
|
||||
public final class BungeeSonarCommand extends Command implements TabExecutor, SonarCommand {
|
||||
public BungeeSonarCommand() {
|
||||
super("sonar");
|
||||
cacheHelpMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"redundantSuppression"})
|
||||
public void execute(final @NotNull CommandSender sender, final String[] args) {
|
||||
if (!(sender instanceof ConsoleCommandSender)) {
|
||||
// Check if the player actually has the permission to run the command
|
||||
if (!sender.hasPermission("sonar.command")) {
|
||||
sender.sendMessage(new TextComponent(Sonar.get().getConfig().getNoPermission()));
|
||||
return;
|
||||
}
|
||||
// Checking if it contains will only break more since it can throw
|
||||
// a NullPointerException if the cache is being accessed from parallel threads
|
||||
DELAY.cleanUp(); // Clean up the cache
|
||||
@@ -117,16 +63,14 @@ public final class SonarCommand extends Command implements TabExecutor, SonarBas
|
||||
// Spamming should be prevented, especially if some heavy operations are done,
|
||||
// which is not the case here but let's still stay safe!
|
||||
if (mapTimestamp > 0L) {
|
||||
sender.sendMessage(new TextComponent(Sonar.get().getConfig().COMMAND_COOL_DOWN));
|
||||
sender.sendMessage(new TextComponent(Sonar.get().getConfig().getCommandCoolDown()));
|
||||
|
||||
// Format delay
|
||||
final long timestamp = System.currentTimeMillis();
|
||||
final double left = 0.5D - (timestamp - mapTimestamp) / 1000D;
|
||||
|
||||
sender.sendMessage(new TextComponent(
|
||||
Sonar.get().getConfig().COMMAND_COOL_DOWN_LEFT
|
||||
.replace("%time-left%", Sonar.DECIMAL_FORMAT.format(left))
|
||||
));
|
||||
sender.sendMessage(new TextComponent(Sonar.get().getConfig().getCommandCoolDownLeft()
|
||||
.replace("%time-left%", Sonar.DECIMAL_FORMAT.format(left))));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -135,18 +79,7 @@ public final class SonarCommand extends Command implements TabExecutor, SonarBas
|
||||
|
||||
Optional<Subcommand> subcommand = Optional.empty();
|
||||
|
||||
final InvocationSender invocationSender = new InvocationSender() {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return sender.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(final String message) {
|
||||
sender.sendMessage(new TextComponent(message));
|
||||
}
|
||||
};
|
||||
final InvocationSource invocationSource = new BungeeInvocationSource(sender);
|
||||
|
||||
if (args.length > 0) {
|
||||
// Search subcommand if command arguments are present
|
||||
@@ -161,8 +94,8 @@ public final class SonarCommand extends Command implements TabExecutor, SonarBas
|
||||
if (!subcommand.get().getInfo().onlyConsole()
|
||||
&& !sender.hasPermission(subcommand.get().getPermission())
|
||||
) {
|
||||
invocationSender.sendMessage(
|
||||
Sonar.get().getConfig().SUB_COMMAND_NO_PERM
|
||||
invocationSource.sendMessage(
|
||||
Sonar.get().getConfig().getSubCommandNoPerm()
|
||||
.replace("%permission%", subcommand.get().getPermission())
|
||||
);
|
||||
return;
|
||||
@@ -172,24 +105,22 @@ public final class SonarCommand extends Command implements TabExecutor, SonarBas
|
||||
|
||||
subcommand.ifPresent(sub -> {
|
||||
if (sub.getInfo().onlyPlayers() && !(sender instanceof ProxiedPlayer)) {
|
||||
invocationSender.sendMessage(Sonar.get().getConfig().PLAYERS_ONLY);
|
||||
invocationSource.sendMessage(Sonar.get().getConfig().getPlayersOnly());
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub.getInfo().onlyConsole() && !(sender instanceof ConsoleCommandSender)) {
|
||||
invocationSender.sendMessage(Sonar.get().getConfig().CONSOLE_ONLY);
|
||||
invocationSource.sendMessage(Sonar.get().getConfig().getConsoleOnly());
|
||||
return;
|
||||
}
|
||||
|
||||
final CommandInvocation commandInvocation = new CommandInvocation(invocationSender, sub, args);
|
||||
final CommandInvocation commandInvocation = new CommandInvocation(invocationSource, sub, args);
|
||||
|
||||
// The subcommands has arguments which are not present in the executed command
|
||||
if (sub.getInfo().arguments().length > 0
|
||||
&& commandInvocation.getRawArguments().length <= 1) {
|
||||
invocationSender.sendMessage(
|
||||
Sonar.get().getConfig().INCORRECT_COMMAND_USAGE
|
||||
.replace("%usage%", sub.getInfo().name() + " (" + sub.getArguments() + ")")
|
||||
);
|
||||
invocationSource.sendMessage(Sonar.get().getConfig().getIncorrectCommandUsage()
|
||||
.replace("%usage%", sub.getInfo().name() + " (" + sub.getArguments() + ")"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -202,14 +133,18 @@ public final class SonarCommand extends Command implements TabExecutor, SonarBas
|
||||
// Re-use the old, cached help message since we don't want to scan
|
||||
// for each subcommand and it's arguments/attributes every time
|
||||
// someone runs /sonar since the subcommand don't change
|
||||
for (final Object component : CACHED_HELP_MESSAGE) {
|
||||
sender.sendMessage((TextComponent) component);
|
||||
for (final Component component : CACHED_HELP_MESSAGE) {
|
||||
invocationSource.sendMessage(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> onTabComplete(final CommandSender sender, final String @NotNull [] args) {
|
||||
// Do not allow tab completion if the player does not have the required permission
|
||||
if (!sender.hasPermission("sonar.command")) {
|
||||
return emptyList();
|
||||
}
|
||||
if (args.length <= 1) {
|
||||
if (TAB_SUGGESTIONS.isEmpty()) {
|
||||
for (final Subcommand subcommand : Sonar.get().getSubcommandRegistry().getSubcommands()) {
|
||||
+27
-24
@@ -21,8 +21,11 @@ import com.velocitypowered.natives.compression.VelocityCompressor;
|
||||
import com.velocitypowered.natives.util.Natives;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.json.JSONComponentSerializer;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.md_5.bungee.api.event.PostLoginEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.compress.PacketCompressor;
|
||||
@@ -30,17 +33,18 @@ import net.md_5.bungee.compress.PacketDecompressor;
|
||||
import net.md_5.bungee.connection.InitialHandler;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.protocol.packet.Kick;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.ReflectiveOperationException;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.fallback.Fallback;
|
||||
import xyz.jonesdev.sonar.bungee.fallback.compress.FallbackPacketCompressor;
|
||||
import xyz.jonesdev.sonar.bungee.fallback.compress.FallbackPacketDecompressor;
|
||||
import xyz.jonesdev.sonar.bungee.fallback.handler.FallbackInitialHandler;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import static net.md_5.bungee.netty.PipelineUtils.FRAME_PREPENDER;
|
||||
import static xyz.jonesdev.sonar.bungee.fallback.FallbackListener.CachedMessages.LOCKDOWN_DISCONNECT;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class FallbackListener implements Listener {
|
||||
@@ -57,36 +61,35 @@ public final class FallbackListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
public static class CachedMessages {
|
||||
static TextComponent LOCKDOWN_DISCONNECT;
|
||||
|
||||
public static void update() {
|
||||
LOCKDOWN_DISCONNECT = new TextComponent(Sonar.get().getConfig().LOCKDOWN_DISCONNECT);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@SuppressWarnings("deprecation")
|
||||
public void handle(final @NotNull PostLoginEvent event) throws Throwable {
|
||||
if (Sonar.get().getConfig().LOCKDOWN_ENABLED) {
|
||||
if (Sonar.get().getConfig().isLockdownEnabled()) {
|
||||
if (!event.getPlayer().hasPermission("sonar.lockdown")) {
|
||||
event.getPlayer().disconnect(LOCKDOWN_DISCONNECT);
|
||||
final PendingConnection pendingConnection = event.getPlayer().getPendingConnection();
|
||||
// Try to close the channel with a custom serialized disconnect component
|
||||
if (pendingConnection instanceof FallbackInitialHandler) {
|
||||
final FallbackInitialHandler fallbackInitialHandler = (FallbackInitialHandler) pendingConnection;
|
||||
final Component component = Sonar.get().getConfig().getLockdownDisconnect();
|
||||
final String serialized = JSONComponentSerializer.json().serialize(component);
|
||||
fallbackInitialHandler.closeWith(new Kick(serialized));
|
||||
} else {
|
||||
// Fallback by disconnecting without a message
|
||||
pendingConnection.disconnect();
|
||||
Sonar.get().getLogger().warn("Fallback handler of {} is missing", event.getPlayer().getName());
|
||||
}
|
||||
|
||||
if (Sonar.get().getConfig().LOCKDOWN_LOG_ATTEMPTS) {
|
||||
Sonar.get().getLogger().info(
|
||||
Sonar.get().getConfig().LOCKDOWN_CONSOLE_LOG
|
||||
.replace("%player%", event.getPlayer().getName())
|
||||
.replace("%ip%", Sonar.get().getConfig()
|
||||
.formatAddress(event.getPlayer().getAddress().getAddress()))
|
||||
.replace("%protocol%",
|
||||
String.valueOf(event.getPlayer().getPendingConnection().getVersion()))
|
||||
);
|
||||
if (Sonar.get().getConfig().isLockdownLogAttempts()) {
|
||||
Sonar.get().getLogger().info(Sonar.get().getConfig().getLockdownConsoleLog()
|
||||
.replace("%player%", event.getPlayer().getName())
|
||||
.replace("%ip%", Sonar.get().getConfig()
|
||||
.formatAddress(event.getPlayer().getAddress().getAddress()))
|
||||
.replace("%protocol%",
|
||||
String.valueOf(event.getPlayer().getPendingConnection().getVersion())));
|
||||
}
|
||||
return;
|
||||
} else if (Sonar.get().getConfig().LOCKDOWN_ENABLE_NOTIFY) {
|
||||
event.getPlayer().sendMessage(
|
||||
new TextComponent(Sonar.get().getConfig().LOCKDOWN_NOTIFICATION)
|
||||
);
|
||||
} else if (Sonar.get().getConfig().isLockdownEnableNotify()) {
|
||||
event.getPlayer().sendMessage(new TextComponent(Sonar.get().getConfig().getLockdownNotification()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -22,7 +22,7 @@ import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.Fallback;
|
||||
@@ -45,8 +45,8 @@ public final class FallbackUserWrapper implements FallbackUser<ChannelWrapper, F
|
||||
private final ProtocolVersion protocolVersion;
|
||||
|
||||
@Override
|
||||
public void disconnect(final @NotNull String reason) {
|
||||
connection.closeWith(Disconnect.create(TextComponent.toLegacyText(new TextComponent(reason))));
|
||||
public void disconnect(final @NotNull Component reason) {
|
||||
connection.closeWith(Disconnect.create(reason));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-6
@@ -27,13 +27,10 @@ import net.md_5.bungee.netty.PipelineUtils;
|
||||
import net.md_5.bungee.protocol.Varint21LengthFieldPrepender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.ReflectiveOperationException;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.config.SonarConfiguration;
|
||||
import xyz.jonesdev.sonar.bungee.fallback.varint.Varint21FrameDecoder;
|
||||
import xyz.jonesdev.sonar.common.fallback.FallbackTimeoutHandler;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static net.md_5.bungee.netty.PipelineUtils.*;
|
||||
@@ -42,8 +39,7 @@ import static net.md_5.bungee.netty.PipelineUtils.*;
|
||||
public final class BaseChannelInitializer extends ChannelInitializer<Channel> {
|
||||
public static final BaseChannelInitializer INSTANCE = new BaseChannelInitializer();
|
||||
|
||||
// cached object of our configuration to instantly access it
|
||||
private static final SonarConfiguration CONFIG = Objects.requireNonNull(Sonar.get().getConfig());
|
||||
private static final boolean REPLACE_VAR_INT_DECODER = !Boolean.getBoolean("sonar.do-not-replace-decoder");
|
||||
|
||||
private static final WriteBufferWaterMark MARK;
|
||||
private static final Varint21LengthFieldPrepender FRAME_ENCODER;
|
||||
@@ -75,7 +71,7 @@ public final class BaseChannelInitializer extends ChannelInitializer<Channel> {
|
||||
channel.config().setAllocator(PooledByteBufAllocator.DEFAULT);
|
||||
channel.config().setWriteBufferWaterMark(MARK);
|
||||
|
||||
channel.pipeline().addLast(FRAME_DECODER, CONFIG.REPLACE_VAR_INT_DECODER
|
||||
channel.pipeline().addLast(FRAME_DECODER, REPLACE_VAR_INT_DECODER
|
||||
? new Varint21FrameDecoder() : new net.md_5.bungee.protocol.Varint21FrameDecoder());
|
||||
channel.pipeline().addLast(TIMEOUT_HANDLER, new FallbackTimeoutHandler(
|
||||
BungeeCord.getInstance().config.getTimeout(), TimeUnit.MILLISECONDS
|
||||
|
||||
@@ -19,6 +19,7 @@ package xyz.jonesdev.sonar.common.boot;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.cappuccino.Cappuccino;
|
||||
import xyz.jonesdev.cappuccino.ExpiringCache;
|
||||
@@ -41,6 +42,7 @@ import java.util.concurrent.TimeUnit;
|
||||
@RequiredArgsConstructor
|
||||
public abstract class SonarBootstrap<T> implements Sonar {
|
||||
private T plugin;
|
||||
@Setter
|
||||
private Verbose verboseHandler;
|
||||
private SonarConfiguration config;
|
||||
private VerifiedPlayerController verifiedPlayerController;
|
||||
@@ -88,6 +90,7 @@ public abstract class SonarBootstrap<T> implements Sonar {
|
||||
} catch (Throwable throwable) {
|
||||
// An error has occurred
|
||||
getLogger().error("An error has occurred while launching Sonar: {}", throwable);
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +102,7 @@ public abstract class SonarBootstrap<T> implements Sonar {
|
||||
|
||||
// Warn player if they reloaded and changed the database type
|
||||
if (getVerifiedPlayerController() != null
|
||||
&& getVerifiedPlayerController().getCachedDatabaseType() != getConfig().DATABASE_TYPE) {
|
||||
&& getVerifiedPlayerController().getCachedDatabaseType() != getConfig().getDatabaseType()) {
|
||||
Sonar.get().getLogger().warn("Reloading the server after changing the database type"
|
||||
+ " is generally not recommended as it can sometimes cause data loss.");
|
||||
}
|
||||
@@ -109,7 +112,7 @@ public abstract class SonarBootstrap<T> implements Sonar {
|
||||
|
||||
// Update ratelimiter
|
||||
final ExpiringCache<InetAddress> expiringCache = Cappuccino.buildExpiring(
|
||||
getConfig().VERIFICATION_DELAY, TimeUnit.MILLISECONDS, 250L
|
||||
getConfig().getVerificationDelay(), TimeUnit.MILLISECONDS, 250L
|
||||
);
|
||||
FallbackRatelimiter.INSTANCE.setExpiringCache(expiringCache);
|
||||
|
||||
|
||||
+13
-17
@@ -127,7 +127,7 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
private static boolean validateClientLocale(final @SuppressWarnings("unused") @NotNull FallbackUser<?, ?> user,
|
||||
final String locale) {
|
||||
// Check the client locale by performing a simple regex check on it
|
||||
final Pattern pattern = Sonar.get().getConfig().VALID_LOCALE_REGEX;
|
||||
final Pattern pattern = Sonar.get().getConfig().getValidLocaleRegex();
|
||||
return pattern.matcher(locale).matches(); // Disallow non-ascii characters (by default)
|
||||
}
|
||||
|
||||
@@ -140,9 +140,9 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
if (user.getProtocolVersion().compareTo(MINECRAFT_1_8) < 0) return true;
|
||||
// No need to check for empty or too long client brands since
|
||||
// ProtocolUtil#readString already does exactly that.
|
||||
final String read = ProtocolUtil.readString(content, Sonar.get().getConfig().MAXIMUM_BRAND_LENGTH);
|
||||
final String read = ProtocolUtil.readString(content, Sonar.get().getConfig().getMaximumBrandLength());
|
||||
// Regex pattern for validating client brands
|
||||
final Pattern pattern = Sonar.get().getConfig().VALID_BRAND_REGEX;
|
||||
final Pattern pattern = Sonar.get().getConfig().getValidBrandRegex();
|
||||
return !read.equals("Vanilla") // The normal brand is always lowercase
|
||||
&& pattern.matcher(read).matches(); // Disallow non-ascii characters (by default)
|
||||
} catch (DecoderException exception) {
|
||||
@@ -156,12 +156,12 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
@Override
|
||||
public void handle(final @NotNull FallbackPacket packet) {
|
||||
// Check if the player is not sending a ton of packets to the server
|
||||
final int maxPackets = Sonar.get().getConfig().MAXIMUM_LOGIN_PACKETS
|
||||
+ Sonar.get().getConfig().MAXIMUM_MOVEMENT_TICKS;
|
||||
final int maxPackets = Sonar.get().getConfig().getMaximumLoginPackets()
|
||||
+ Sonar.get().getConfig().getMaximumMovementTicks();
|
||||
checkFrame(++receivedPackets < maxPackets, "too many packets");
|
||||
|
||||
// Check for timeout since the player could be sending packets but not important ones
|
||||
final long timeout = Sonar.get().getConfig().VERIFICATION_TIMEOUT;
|
||||
final long timeout = Sonar.get().getConfig().getVerificationTimeout();
|
||||
// Check if the time limit has exceeded
|
||||
if (login.elapsed(timeout)) {
|
||||
user.getChannel().close();
|
||||
@@ -231,7 +231,7 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
checkFrame(transaction.getId() == transactionId, "invalid transaction id");
|
||||
|
||||
// Checking gravity is disabled, just finish verification
|
||||
if (!Sonar.get().getConfig().CHECK_GRAVITY) {
|
||||
if (!Sonar.get().getConfig().isCheckGravity()) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
@@ -310,7 +310,7 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
if (deltaY > 0.07) {
|
||||
// Verify the player if they sent correct movement packets
|
||||
if (movementTick++ >= MAX_MOVEMENT_TICK) {
|
||||
if (Sonar.get().getConfig().CHECK_COLLISIONS) {
|
||||
if (Sonar.get().getConfig().isCheckCollisions()) {
|
||||
if (state != State.COLLISIONS) {
|
||||
// Set the state to COLLISIONS to avoid false positives
|
||||
// and go on with the flow of the verification.
|
||||
@@ -366,21 +366,17 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
user.getFallback().getConnected().remove(username);
|
||||
|
||||
// Add verified player to the database
|
||||
final VerifiedPlayer verifiedPlayer = new VerifiedPlayer(
|
||||
user.getInetAddress().toString(), uuid, login.getStart()
|
||||
);
|
||||
final VerifiedPlayer verifiedPlayer = new VerifiedPlayer(user.getInetAddress().toString(), uuid, login.getStart());
|
||||
Sonar.get().getVerifiedPlayerController().add(verifiedPlayer);
|
||||
|
||||
// Call the VerifySuccessEvent for external API usage
|
||||
Sonar.get().getEventManager().publish(new UserVerifySuccessEvent(username, uuid, user, login.delay()));
|
||||
|
||||
// Disconnect player with the verification success message
|
||||
user.disconnect(Sonar.get().getConfig().VERIFICATION_SUCCESS);
|
||||
user.disconnect(Sonar.get().getConfig().getVerificationSuccess());
|
||||
|
||||
user.getFallback().getLogger().info(
|
||||
Sonar.get().getConfig().VERIFICATION_SUCCESSFUL_LOG
|
||||
.replace("%name%", username)
|
||||
.replace("%time%", login.toString())
|
||||
);
|
||||
user.getFallback().getLogger().info(Sonar.get().getConfig().getVerificationSuccessfulLog()
|
||||
.replace("%name%", username)
|
||||
.replace("%time%", login.toString()));
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -99,7 +99,7 @@ public class FallbackPreparer {
|
||||
JOIN_GAME_1_19_4 = createJoinGamePacket(MINECRAFT_1_19_4);
|
||||
JOIN_GAME_1_20 = createJoinGamePacket(MINECRAFT_1_20);
|
||||
|
||||
MAX_MOVEMENT_TICK = Sonar.get().getConfig().MAXIMUM_MOVEMENT_TICKS;
|
||||
MAX_MOVEMENT_TICK = Sonar.get().getConfig().getMaximumMovementTicks();
|
||||
PREPARED_MOVEMENT_PACKETS = new double[MAX_MOVEMENT_TICK + 1];
|
||||
|
||||
for (int i = 0; i < MAX_MOVEMENT_TICK + 1; i++) {
|
||||
@@ -184,7 +184,7 @@ public class FallbackPreparer {
|
||||
final JoinGame joinGame = new JoinGame();
|
||||
|
||||
joinGame.setLevelType("flat");
|
||||
joinGame.setGamemode(Sonar.get().getConfig().GAMEMODE_ID);
|
||||
joinGame.setGamemode(Sonar.get().getConfig().getGamemodeId());
|
||||
joinGame.setDimension(0);
|
||||
joinGame.setReducedDebugInfo(true);
|
||||
return joinGame;
|
||||
@@ -196,7 +196,7 @@ public class FallbackPreparer {
|
||||
final JoinGame joinGame = new JoinGame();
|
||||
|
||||
joinGame.setLevelType("flat");
|
||||
joinGame.setGamemode(Sonar.get().getConfig().GAMEMODE_ID);
|
||||
joinGame.setGamemode(Sonar.get().getConfig().getGamemodeId());
|
||||
joinGame.setDimension(0);
|
||||
joinGame.setReducedDebugInfo(true);
|
||||
joinGame.setDimensionInfo(DIMENSION);
|
||||
|
||||
+4
-2
@@ -22,6 +22,8 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.json.JSONComponentSerializer;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
@@ -48,7 +50,7 @@ public final class Disconnect implements FallbackPacket {
|
||||
writeString(byteBuf, Objects.requireNonNull(reason));
|
||||
}
|
||||
|
||||
public static Disconnect create(final String serialized) {
|
||||
return new Disconnect(serialized);
|
||||
public static Disconnect create(final Component component) {
|
||||
return new Disconnect(JSONComponentSerializer.json().serialize(component));
|
||||
}
|
||||
}
|
||||
+17
-22
@@ -44,15 +44,14 @@ public final class BlacklistCommand extends Subcommand {
|
||||
switch (invocation.getRawArguments()[1].toLowerCase()) {
|
||||
case "add": {
|
||||
if (invocation.getRawArguments().length <= 2) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().INCORRECT_COMMAND_USAGE
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getIncorrectCommandUsage()
|
||||
.replace("%usage%", "blacklist add <IP address>"));
|
||||
return;
|
||||
}
|
||||
|
||||
final String rawInetAddress = invocation.getRawArguments()[2];
|
||||
|
||||
if (!IP_PATTERN.matcher(rawInetAddress).matches()) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().INCORRECT_IP_ADDRESS);
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getIncorrectIpAddress());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -61,41 +60,40 @@ public final class BlacklistCommand extends Subcommand {
|
||||
inetAddress = InetAddress.getByName(rawInetAddress);
|
||||
|
||||
if (inetAddress.isAnyLocalAddress() || inetAddress.isLoopbackAddress()) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().ILLEGAL_IP_ADDRESS);
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getIllegalIpAddress());
|
||||
return;
|
||||
}
|
||||
} catch (UnknownHostException exception) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().ILLEGAL_IP_ADDRESS);
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getIllegalIpAddress());
|
||||
return;
|
||||
}
|
||||
|
||||
if (SONAR.getFallback().getBlacklisted().has(inetAddress.toString())) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().BLACKLIST_DUPLICATE);
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getBlacklistDuplicate());
|
||||
return;
|
||||
}
|
||||
|
||||
if (SONAR.getVerifiedPlayerController().has(inetAddress)) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().BLACKLIST_ADD_WARNING
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getBlacklistAddWarning()
|
||||
.replace("%ip%", rawInetAddress));
|
||||
}
|
||||
|
||||
SONAR.getFallback().getBlacklisted().put(inetAddress.toString());
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().BLACKLIST_ADD
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getBlacklistAdd()
|
||||
.replace("%ip%", rawInetAddress));
|
||||
break;
|
||||
}
|
||||
|
||||
case "remove": {
|
||||
if (invocation.getRawArguments().length <= 2) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().INCORRECT_COMMAND_USAGE
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getIncorrectCommandUsage()
|
||||
.replace("%usage%", "blacklist remove <IP address>"));
|
||||
return;
|
||||
}
|
||||
|
||||
final String rawInetAddress = invocation.getRawArguments()[2];
|
||||
|
||||
if (!IP_PATTERN.matcher(rawInetAddress).matches()) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().INCORRECT_IP_ADDRESS);
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getIncorrectIpAddress());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -104,21 +102,21 @@ public final class BlacklistCommand extends Subcommand {
|
||||
inetAddress = InetAddress.getByName(rawInetAddress);
|
||||
|
||||
if (inetAddress.isAnyLocalAddress() || inetAddress.isLoopbackAddress()) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().ILLEGAL_IP_ADDRESS);
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getIllegalIpAddress());
|
||||
return;
|
||||
}
|
||||
} catch (UnknownHostException exception) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().ILLEGAL_IP_ADDRESS);
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getIllegalIpAddress());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SONAR.getFallback().getBlacklisted().has(inetAddress.toString())) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().BLACKLIST_NOT_FOUND);
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getBlacklistNotFound());
|
||||
return;
|
||||
}
|
||||
|
||||
SONAR.getFallback().getBlacklisted().invalidate(inetAddress.toString());
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().BLACKLIST_REMOVE
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getBlacklistRemove()
|
||||
.replace("%ip%", rawInetAddress));
|
||||
break;
|
||||
}
|
||||
@@ -127,21 +125,18 @@ public final class BlacklistCommand extends Subcommand {
|
||||
final int blacklisted = SONAR.getFallback().getBlacklisted().estimatedSize();
|
||||
|
||||
if (blacklisted == 0) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().BLACKLIST_EMPTY);
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getBlacklistEmpty());
|
||||
return;
|
||||
}
|
||||
|
||||
SONAR.getFallback().getBlacklisted().invalidateAll();
|
||||
|
||||
invocation.getSender().sendMessage(
|
||||
SONAR.getConfig().BLACKLIST_CLEARED
|
||||
.replace("%removed%", Sonar.DECIMAL_FORMAT.format(blacklisted))
|
||||
);
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getBlacklistCleared()
|
||||
.replace("%removed%", Sonar.DECIMAL_FORMAT.format(blacklisted)));
|
||||
break;
|
||||
}
|
||||
|
||||
case "size": {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().BLACKLIST_SIZE
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getBlacklistSize()
|
||||
.replace("%amount%", Sonar.DECIMAL_FORMAT.format(SONAR.getFallback().getBlacklisted().estimatedSize())));
|
||||
break;
|
||||
}
|
||||
|
||||
+5
-7
@@ -30,14 +30,12 @@ public final class LockdownCommand extends Subcommand {
|
||||
|
||||
@Override
|
||||
public void execute(final @NotNull CommandInvocation invocation) {
|
||||
SONAR.getConfig().LOCKDOWN_ENABLED = !SONAR.getConfig().LOCKDOWN_ENABLED;
|
||||
SONAR.getConfig().getGeneralConfig().set("general.lockdown.enabled", SONAR.getConfig().LOCKDOWN_ENABLED);
|
||||
final boolean newState = !SONAR.getConfig().isLockdownEnabled();
|
||||
SONAR.getConfig().setLockdownEnabled(newState);
|
||||
SONAR.getConfig().getGeneralConfig().set("general.lockdown.enabled", newState);
|
||||
SONAR.getConfig().getGeneralConfig().save();
|
||||
|
||||
invocation.getSender().sendMessage(
|
||||
SONAR.getConfig().LOCKDOWN_ENABLED
|
||||
? SONAR.getConfig().LOCKDOWN_ACTIVATED
|
||||
: SONAR.getConfig().LOCKDOWN_DEACTIVATED
|
||||
);
|
||||
invocation.getSender().sendMessage(newState
|
||||
? SONAR.getConfig().getLockdownActivated() : SONAR.getConfig().getLockdownDeactivated());
|
||||
}
|
||||
}
|
||||
+5
-7
@@ -21,6 +21,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.command.CommandInvocation;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.Subcommand;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.SubcommandInfo;
|
||||
import xyz.jonesdev.sonar.api.timer.SystemTimer;
|
||||
|
||||
@SubcommandInfo(
|
||||
name = "reload",
|
||||
@@ -30,15 +31,12 @@ public final class ReloadCommand extends Subcommand {
|
||||
|
||||
@Override
|
||||
public void execute(final @NotNull CommandInvocation invocation) {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
final SystemTimer timer = new SystemTimer();
|
||||
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().RELOADING);
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getReloading());
|
||||
SONAR.reload();
|
||||
|
||||
final long timeTaken = System.currentTimeMillis() - startTime;
|
||||
invocation.getSender().sendMessage(
|
||||
SONAR.getConfig().RELOADED
|
||||
.replace("%taken%", String.valueOf(timeTaken))
|
||||
);
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getReloaded()
|
||||
.replace("%taken%", String.valueOf(timer.delay())));
|
||||
}
|
||||
}
|
||||
+9
-13
@@ -19,7 +19,7 @@ package xyz.jonesdev.sonar.common.subcommand.impl;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.command.CommandInvocation;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSender;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.Subcommand;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.SubcommandInfo;
|
||||
|
||||
@@ -34,33 +34,29 @@ public final class VerboseCommand extends Subcommand {
|
||||
|
||||
@Override
|
||||
public void execute(final @NotNull CommandInvocation invocation) {
|
||||
InvocationSender verboseSubscriber = invocation.getSender();
|
||||
InvocationSource verboseSubscriber = invocation.getSender();
|
||||
|
||||
// Support for '/sonar verbose [username]'
|
||||
if (invocation.getRawArguments().length >= 2) {
|
||||
final Optional<InvocationSender> optional = SONAR.getServer().getOnlinePlayer(invocation.getRawArguments()[1]);
|
||||
final Optional<InvocationSource> optional = SONAR.getServer().getOnlinePlayer(invocation.getRawArguments()[1]);
|
||||
if (optional.isPresent()) verboseSubscriber = optional.get();
|
||||
}
|
||||
|
||||
if (SONAR.getVerboseHandler().isSubscribed(verboseSubscriber.getName())) {
|
||||
SONAR.getVerboseHandler().unsubscribe(verboseSubscriber.getName());
|
||||
if (verboseSubscriber != invocation.getSender()) {
|
||||
verboseSubscriber.sendMessage(
|
||||
SONAR.getConfig().VERBOSE_UNSUBSCRIBED_OTHER
|
||||
.replace("%player%", verboseSubscriber.getName())
|
||||
);
|
||||
verboseSubscriber.sendMessage(SONAR.getConfig().getVerboseUnsubscribedOther()
|
||||
.replace("%player%", verboseSubscriber.getName()));
|
||||
}
|
||||
verboseSubscriber.sendMessage(SONAR.getConfig().VERBOSE_UNSUBSCRIBED);
|
||||
verboseSubscriber.sendMessage(SONAR.getConfig().getVerboseUnsubscribed());
|
||||
return;
|
||||
}
|
||||
|
||||
if (verboseSubscriber != invocation.getSender()) {
|
||||
verboseSubscriber.sendMessage(
|
||||
SONAR.getConfig().VERBOSE_SUBSCRIBED_OTHER
|
||||
.replace("%player%", verboseSubscriber.getName())
|
||||
);
|
||||
verboseSubscriber.sendMessage(SONAR.getConfig().getVerboseSubscribedOther()
|
||||
.replace("%player%", verboseSubscriber.getName()));
|
||||
}
|
||||
verboseSubscriber.sendMessage(SONAR.getConfig().VERBOSE_SUBSCRIBED);
|
||||
verboseSubscriber.sendMessage(SONAR.getConfig().getVerboseSubscribed());
|
||||
SONAR.getVerboseHandler().subscribe(verboseSubscriber.getName());
|
||||
}
|
||||
}
|
||||
+19
-22
@@ -46,27 +46,26 @@ public final class VerifiedCommand extends Subcommand {
|
||||
switch (invocation.getRawArguments()[1].toLowerCase()) {
|
||||
case "history": {
|
||||
if (invocation.getRawArguments().length <= 2) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().INCORRECT_COMMAND_USAGE
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getIncorrectCommandUsage()
|
||||
.replace("%usage%", "verified history <IP address>"));
|
||||
return;
|
||||
}
|
||||
|
||||
final String inetAddress = invocation.getRawArguments()[2];
|
||||
|
||||
if (!IP_PATTERN.matcher(inetAddress).matches()) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().INCORRECT_IP_ADDRESS);
|
||||
final String rawInetAddress = invocation.getRawArguments()[2];
|
||||
if (!IP_PATTERN.matcher(rawInetAddress).matches()) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getIncorrectIpAddress());
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't need to parse this, so we can just use the string.
|
||||
final String realInetAddress = "/" + inetAddress;
|
||||
final String realInetAddress = "/" + rawInetAddress;
|
||||
|
||||
if (!SONAR.getVerifiedPlayerController().has(realInetAddress)) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().VERIFIED_NOT_FOUND);
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getVerifiedNotFound());
|
||||
return;
|
||||
}
|
||||
|
||||
invocation.getSender().sendMessage("§ePrevious UUIDs for " + inetAddress + ":");
|
||||
invocation.getSender().sendMessage("§ePrevious UUIDs for " + rawInetAddress + ":");
|
||||
|
||||
for (final UUID uuid : SONAR.getVerifiedPlayerController().getUUIDs(realInetAddress)) {
|
||||
invocation.getSender().sendMessage(" §7▪ §f" + uuid.toString());
|
||||
@@ -76,28 +75,27 @@ public final class VerifiedCommand extends Subcommand {
|
||||
|
||||
case "remove": {
|
||||
if (invocation.getRawArguments().length <= 2) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().INCORRECT_COMMAND_USAGE
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getIncorrectCommandUsage()
|
||||
.replace("%usage%", "verified remove <IP address>"));
|
||||
return;
|
||||
}
|
||||
|
||||
final String inetAddress = invocation.getRawArguments()[2];
|
||||
|
||||
if (!IP_PATTERN.matcher(inetAddress).matches()) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().INCORRECT_IP_ADDRESS);
|
||||
final String rawInetAddress = invocation.getRawArguments()[2];
|
||||
if (!IP_PATTERN.matcher(rawInetAddress).matches()) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getIncorrectIpAddress());
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't need to parse this, so we can just use the string.
|
||||
final String realInetAddress = "/" + inetAddress;
|
||||
final String realInetAddress = "/" + rawInetAddress;
|
||||
|
||||
if (LOCK.contains(realInetAddress)) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().VERIFIED_BLOCKED);
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getVerifiedBlocked());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SONAR.getVerifiedPlayerController().has(realInetAddress)) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().VERIFIED_NOT_FOUND);
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getVerifiedNotFound());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -106,8 +104,8 @@ public final class VerifiedCommand extends Subcommand {
|
||||
SONAR.getVerifiedPlayerController().remove(realInetAddress);
|
||||
LOCK.remove(realInetAddress);
|
||||
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().VERIFIED_REMOVE
|
||||
.replace("%ip%", inetAddress));
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getVerifiedRemove()
|
||||
.replace("%ip%", rawInetAddress));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -115,19 +113,18 @@ public final class VerifiedCommand extends Subcommand {
|
||||
final int verified = SONAR.getVerifiedPlayerController().estimatedSize();
|
||||
|
||||
if (verified == 0) {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().VERIFIED_EMPTY);
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getVerifiedEmpty());
|
||||
return;
|
||||
}
|
||||
|
||||
SONAR.getVerifiedPlayerController().clearAll();
|
||||
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().VERIFIED_CLEARED
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getVerifiedCleared()
|
||||
.replace("%removed%", Sonar.DECIMAL_FORMAT.format(verified)));
|
||||
break;
|
||||
}
|
||||
|
||||
case "size": {
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().VERIFIED_SIZE
|
||||
invocation.getSender().sendMessage(SONAR.getConfig().getVerifiedSize()
|
||||
.replace("%amount%", Sonar.DECIMAL_FORMAT.format(SONAR.getVerifiedPlayerController().estimatedSize())));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4,17 +4,34 @@ dependencies {
|
||||
compileOnly(project(":api"))
|
||||
compileOnly(project(":common"))
|
||||
|
||||
compileOnly("com.velocitypowered:velocity-proxy:$velocityVersion") // Proxy module
|
||||
compileOnly("com.velocitypowered:velocity-proxy:$velocityVersion")
|
||||
testCompileOnly("com.velocitypowered:velocity-proxy:$velocityVersion")
|
||||
|
||||
compileOnly("com.velocitypowered:velocity-api:$velocityVersion")
|
||||
annotationProcessor("com.velocitypowered:velocity-api:$velocityVersion")
|
||||
//annotationProcessor("com.velocitypowered:velocity-api:$velocityVersion")
|
||||
|
||||
testCompileOnly("com.velocitypowered:velocity-api:$velocityVersion")
|
||||
testAnnotationProcessor("com.velocitypowered:velocity-api:$velocityVersion")
|
||||
//testAnnotationProcessor("com.velocitypowered:velocity-api:$velocityVersion")
|
||||
|
||||
// Implement bStats.org for metrics
|
||||
implementation("org.bstats:bstats-velocity:3.0.2")
|
||||
}
|
||||
|
||||
tasks {
|
||||
processResources {
|
||||
val props = mapOf(
|
||||
"name" to rootProject.name,
|
||||
"version" to rootProject.version.toString().split("-")[0],
|
||||
"description" to "Effective Anti-bot plugin for Velocity, BungeeCord, and Bukkit (1.7-latest)",
|
||||
"url" to "https://jonesdev.xyz/discord/",
|
||||
"main" to "xyz.jonesdev.sonar.velocity.SonarVelocityPlugin"
|
||||
)
|
||||
inputs.properties(props)
|
||||
filesMatching("velocity-plugin.json") {
|
||||
expand(props)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
java.sourceCompatibility = JavaVersion.VERSION_11
|
||||
java.targetCompatibility = JavaVersion.VERSION_11
|
||||
@@ -18,15 +18,15 @@
|
||||
package xyz.jonesdev.sonar.velocity;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.SonarPlatform;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSender;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.api.fallback.traffic.TrafficCounter;
|
||||
import xyz.jonesdev.sonar.api.logger.LoggerWrapper;
|
||||
import xyz.jonesdev.sonar.api.server.ServerWrapper;
|
||||
import xyz.jonesdev.sonar.common.boot.SonarBootstrap;
|
||||
import xyz.jonesdev.sonar.velocity.command.SonarCommand;
|
||||
import xyz.jonesdev.sonar.velocity.command.VelocityInvocationSource;
|
||||
import xyz.jonesdev.sonar.velocity.command.VelocitySonarCommand;
|
||||
import xyz.jonesdev.sonar.velocity.fallback.FallbackListener;
|
||||
import xyz.jonesdev.sonar.velocity.verbose.VerboseWrapper;
|
||||
|
||||
@@ -72,30 +72,14 @@ public final class SonarVelocity extends SonarBootstrap<SonarVelocityPlugin> {
|
||||
* <br>
|
||||
* We have to do this, so we can access all necessary API functions.
|
||||
*/
|
||||
public final ServerWrapper server = new ServerWrapper() {
|
||||
public final ServerWrapper server = new ServerWrapper(SonarPlatform.VELOCITY) {
|
||||
|
||||
@Override
|
||||
public SonarPlatform getPlatform() {
|
||||
return SonarPlatform.VELOCITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<InvocationSender> getOnlinePlayer(final String username) {
|
||||
public Optional<InvocationSource> getOnlinePlayer(final String username) {
|
||||
return getPlugin().getServer().getAllPlayers().stream()
|
||||
.filter(player -> player.getUsername().equalsIgnoreCase(username))
|
||||
.findFirst()
|
||||
.map(player -> new InvocationSender() {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return player.getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(final String message) {
|
||||
player.sendMessage(Component.text(message));
|
||||
}
|
||||
});
|
||||
.map(VelocityInvocationSource::new);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -106,7 +90,7 @@ public final class SonarVelocity extends SonarBootstrap<SonarVelocityPlugin> {
|
||||
getPlugin().getMetricsFactory().make(getPlugin(), getServer().getPlatform().getMetricsId());
|
||||
|
||||
// Register Sonar command
|
||||
getPlugin().getServer().getCommandManager().register("sonar", new SonarCommand());
|
||||
getPlugin().getServer().getCommandManager().register("sonar", new VelocitySonarCommand());
|
||||
|
||||
// Register Fallback listener
|
||||
getPlugin().getServer().getEventManager().register(getPlugin(), new FallbackListener(getFallback()));
|
||||
@@ -129,8 +113,5 @@ public final class SonarVelocity extends SonarBootstrap<SonarVelocityPlugin> {
|
||||
|
||||
@Override
|
||||
public void postReload() {
|
||||
|
||||
// Prepare cached messages
|
||||
FallbackListener.CachedMessages.update();
|
||||
}
|
||||
}
|
||||
@@ -31,9 +31,10 @@ import org.slf4j.Logger;
|
||||
import java.nio.file.Path;
|
||||
|
||||
@Getter
|
||||
@Plugin(id = "sonar",
|
||||
@Plugin(
|
||||
id = "sonar",
|
||||
name = "Sonar",
|
||||
version = "2.0.1",
|
||||
version = "$version",
|
||||
authors = {"Jones Development", "Sonar Contributors"},
|
||||
url = "https://jonesdev.xyz/discord/",
|
||||
description = "Effective Anti-bot plugin for Velocity, BungeeCord, and Bukkit (1.7-latest)"
|
||||
|
||||
+10
-19
@@ -15,26 +15,17 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.api.command;
|
||||
package xyz.jonesdev.sonar.velocity.command;
|
||||
|
||||
public interface InvocationSender {
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
|
||||
/**
|
||||
* @return The name of the command executor
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Sends a message to the command executor
|
||||
*
|
||||
* @param message Deserialized message
|
||||
*/
|
||||
void sendMessage(final String message);
|
||||
|
||||
/**
|
||||
* Sends an empty chat message to the command executor
|
||||
*/
|
||||
default void sendMessage() {
|
||||
sendMessage("");
|
||||
public final class VelocityInvocationSource extends InvocationSource {
|
||||
public VelocityInvocationSource(final @NotNull CommandSource sender) {
|
||||
super(sender instanceof Player
|
||||
? ((Player) sender).getUsername()
|
||||
: "Console", sender);
|
||||
}
|
||||
}
|
||||
+27
-83
@@ -21,14 +21,11 @@ import com.velocitypowered.api.command.SimpleCommand;
|
||||
import com.velocitypowered.api.proxy.ConsoleCommandSource;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.command.CommandInvocation;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSender;
|
||||
import xyz.jonesdev.sonar.api.command.SonarBaseCommand;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.api.command.SonarCommand;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.Subcommand;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.argument.Argument;
|
||||
|
||||
@@ -39,54 +36,19 @@ import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
public final class SonarCommand implements SimpleCommand, SonarBaseCommand {
|
||||
static {
|
||||
CACHED_HELP_MESSAGE.addAll(Arrays.asList(
|
||||
Component.text("Running Sonar " + Sonar.get().getVersion()
|
||||
+ " on " + Sonar.get().getServer().getPlatform().getDisplayName()
|
||||
+ ".", NamedTextColor.YELLOW),
|
||||
Component.text("(C) " + COPYRIGHT_YEAR + " Jones Development and Sonar Contributors", NamedTextColor.YELLOW),
|
||||
Component.text("https://github.com/jonesdevelopment/sonar", NamedTextColor.GREEN)
|
||||
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, "https://github.com/jonesdevelopment/sonar")),
|
||||
Component.empty(),
|
||||
Component.text("Need help or have any questions?", NamedTextColor.YELLOW),
|
||||
Component.textOfChildren(
|
||||
Component.text("Open a ticket on the Discord ", NamedTextColor.YELLOW)
|
||||
.hoverEvent(HoverEvent.hoverEvent(HoverEvent.Action.SHOW_TEXT, Component.text("(Click to open Discord)")))
|
||||
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, "https://jonesdev.xyz/discord/")),
|
||||
Component.text("or open a new issue on GitHub.", NamedTextColor.YELLOW)
|
||||
.hoverEvent(HoverEvent.hoverEvent(HoverEvent.Action.SHOW_TEXT, Component.text("(Click to open GitHub)")))
|
||||
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, "https://github.com/jonesdevelopment/sonar" +
|
||||
"/issues"))
|
||||
),
|
||||
Component.empty()
|
||||
));
|
||||
|
||||
Sonar.get().getSubcommandRegistry().getSubcommands().forEach(sub -> {
|
||||
var component = Component.textOfChildren(
|
||||
Component.text(" ▪ ", NamedTextColor.GRAY),
|
||||
Component.text("/sonar " + sub.getInfo().name(), NamedTextColor.GREEN),
|
||||
Component.text(" - ", NamedTextColor.GRAY),
|
||||
Component.text(sub.getInfo().description(), NamedTextColor.WHITE)
|
||||
);
|
||||
|
||||
component = component.clickEvent(
|
||||
ClickEvent.clickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/sonar " + sub.getInfo().name() + " ")
|
||||
).hoverEvent(
|
||||
HoverEvent.hoverEvent(HoverEvent.Action.SHOW_TEXT, Component.text(
|
||||
"§7Only players: §f" + (sub.getInfo().onlyPlayers() ? "§a✔" : "§c✗")
|
||||
+ Sonar.LINE_SEPARATOR + "§7Require console: §f" + (sub.getInfo().onlyConsole() ? "§a✔" : "§c✗")
|
||||
+ Sonar.LINE_SEPARATOR + "§7Permission: §f" + sub.getPermission()
|
||||
+ Sonar.LINE_SEPARATOR + "§7Aliases: §f" + sub.getAliases()
|
||||
))
|
||||
);
|
||||
CACHED_HELP_MESSAGE.add(component);
|
||||
});
|
||||
public final class VelocitySonarCommand implements SimpleCommand, SonarCommand {
|
||||
{
|
||||
cacheHelpMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final @NotNull Invocation invocation) {
|
||||
if (!(invocation.source() instanceof ConsoleCommandSource)) {
|
||||
// Check if the player actually has the permission to run the command
|
||||
if (!invocation.source().hasPermission("sonar.command")) {
|
||||
invocation.source().sendMessage(Component.text(Sonar.get().getConfig().getNoPermission()));
|
||||
return;
|
||||
}
|
||||
// Checking if it contains will only break more since it can throw
|
||||
// a NullPointerException if the cache is being accessed from parallel threads
|
||||
DELAY.cleanUp(); // Clean up the cache
|
||||
@@ -96,16 +58,14 @@ public final class SonarCommand implements SimpleCommand, SonarBaseCommand {
|
||||
// Spamming should be prevented, especially if some heavy operations are done,
|
||||
// which is not the case here but let's still stay safe!
|
||||
if (mapTimestamp > 0L) {
|
||||
invocation.source().sendMessage(Component.text(Sonar.get().getConfig().COMMAND_COOL_DOWN));
|
||||
invocation.source().sendMessage(Component.text(Sonar.get().getConfig().getCommandCoolDown()));
|
||||
|
||||
// Format delay
|
||||
final long timestamp = System.currentTimeMillis();
|
||||
final double left = 0.5D - (timestamp - mapTimestamp) / 1000D;
|
||||
|
||||
invocation.source().sendMessage(Component.text(
|
||||
Sonar.get().getConfig().COMMAND_COOL_DOWN_LEFT
|
||||
.replace("%time-left%", Sonar.DECIMAL_FORMAT.format(left))
|
||||
));
|
||||
invocation.source().sendMessage(Component.text(Sonar.get().getConfig().getCommandCoolDownLeft()
|
||||
.replace("%time-left%", Sonar.DECIMAL_FORMAT.format(left))));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -114,20 +74,7 @@ public final class SonarCommand implements SimpleCommand, SonarBaseCommand {
|
||||
|
||||
Optional<Subcommand> subcommand = Optional.empty();
|
||||
|
||||
final InvocationSender invocationSender = new InvocationSender() {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return invocation.source() instanceof Player
|
||||
? ((Player) invocation.source()).getUsername()
|
||||
: "Console";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(final String message) {
|
||||
invocation.source().sendMessage(Component.text(message));
|
||||
}
|
||||
};
|
||||
final InvocationSource invocationSource = new VelocityInvocationSource(invocation.source());
|
||||
|
||||
if (invocation.arguments().length > 0) {
|
||||
// Search subcommand if command arguments are present
|
||||
@@ -142,8 +89,8 @@ public final class SonarCommand implements SimpleCommand, SonarBaseCommand {
|
||||
if (!subcommand.get().getInfo().onlyConsole()
|
||||
&& !invocation.source().hasPermission(subcommand.get().getPermission())
|
||||
) {
|
||||
invocationSender.sendMessage(
|
||||
Sonar.get().getConfig().SUB_COMMAND_NO_PERM
|
||||
invocationSource.sendMessage(
|
||||
Sonar.get().getConfig().getSubCommandNoPerm()
|
||||
.replace("%permission%", subcommand.get().getPermission())
|
||||
);
|
||||
return;
|
||||
@@ -153,25 +100,23 @@ public final class SonarCommand implements SimpleCommand, SonarBaseCommand {
|
||||
|
||||
subcommand.ifPresent(sub -> {
|
||||
if (sub.getInfo().onlyPlayers() && !(invocation.source() instanceof Player)) {
|
||||
invocationSender.sendMessage(Sonar.get().getConfig().PLAYERS_ONLY);
|
||||
invocationSource.sendMessage(Sonar.get().getConfig().getPlayersOnly());
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub.getInfo().onlyConsole() && !(invocation.source() instanceof ConsoleCommandSource)) {
|
||||
invocationSender.sendMessage(Sonar.get().getConfig().CONSOLE_ONLY);
|
||||
invocationSource.sendMessage(Sonar.get().getConfig().getConsoleOnly());
|
||||
return;
|
||||
}
|
||||
|
||||
final CommandInvocation commandInvocation = new CommandInvocation(invocationSender, sub, invocation.arguments());
|
||||
final CommandInvocation commandInvocation = new CommandInvocation(invocationSource, sub, invocation.arguments());
|
||||
|
||||
// The subcommands has arguments which are not present in the executed command
|
||||
if (sub.getInfo().arguments().length > 0
|
||||
&& commandInvocation.getRawArguments().length <= 1
|
||||
&& sub.getInfo().argumentsRequired()) {
|
||||
invocationSender.sendMessage(
|
||||
Sonar.get().getConfig().INCORRECT_COMMAND_USAGE
|
||||
.replace("%usage%", sub.getInfo().name() + " (" + sub.getArguments() + ")")
|
||||
);
|
||||
invocationSource.sendMessage(Sonar.get().getConfig().getIncorrectCommandUsage()
|
||||
.replace("%usage%", sub.getInfo().name() + " (" + sub.getArguments() + ")"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -184,14 +129,18 @@ public final class SonarCommand implements SimpleCommand, SonarBaseCommand {
|
||||
// Re-use the old, cached help message since we don't want to scan
|
||||
// for each subcommand and it's arguments/attributes every time
|
||||
// someone runs /sonar since the subcommand don't change
|
||||
for (final Object component : CACHED_HELP_MESSAGE) {
|
||||
invocation.source().sendMessage((Component) component);
|
||||
for (final Component component : CACHED_HELP_MESSAGE) {
|
||||
invocationSource.sendMessage(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggest(final @NotNull Invocation invocation) {
|
||||
// Do not allow tab completion if the player does not have the required permission
|
||||
if (!invocation.source().hasPermission("sonar.command")) {
|
||||
return emptyList();
|
||||
}
|
||||
if (invocation.arguments().length <= 1) {
|
||||
if (TAB_SUGGESTIONS.isEmpty()) {
|
||||
for (final Subcommand subcommand : Sonar.get().getSubcommandRegistry().getSubcommands()) {
|
||||
@@ -221,9 +170,4 @@ public final class SonarCommand implements SimpleCommand, SonarBaseCommand {
|
||||
}
|
||||
return emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(final @NotNull Invocation invocation) {
|
||||
return invocation.source().hasPermission("sonar.command");
|
||||
}
|
||||
}
|
||||
+23
-55
@@ -55,34 +55,11 @@ import java.util.concurrent.TimeUnit;
|
||||
import static com.velocitypowered.proxy.network.Connections.*;
|
||||
import static xyz.jonesdev.sonar.api.fallback.FallbackPipelines.FALLBACK_HANDLER;
|
||||
import static xyz.jonesdev.sonar.common.utility.geyser.GeyserUtil.isGeyserConnection;
|
||||
import static xyz.jonesdev.sonar.velocity.fallback.FallbackListener.CachedMessages.*;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class FallbackListener {
|
||||
private final @NotNull Fallback fallback;
|
||||
|
||||
public static class CachedMessages {
|
||||
static Component LOCKDOWN_DISCONNECT;
|
||||
static Component TOO_MANY_ONLINE_PER_IP;
|
||||
static Component TOO_MANY_PLAYERS;
|
||||
static Component BLACKLISTED;
|
||||
static Component ALREADY_VERIFYING;
|
||||
static Component ALREADY_QUEUED;
|
||||
static Component TOO_FAST_RECONNECT;
|
||||
public static Component INVALID_USERNAME;
|
||||
|
||||
public static void update() {
|
||||
ALREADY_VERIFYING = Component.text(Sonar.get().getConfig().ALREADY_VERIFYING);
|
||||
ALREADY_QUEUED = Component.text(Sonar.get().getConfig().ALREADY_QUEUED);
|
||||
TOO_MANY_PLAYERS = Component.text(Sonar.get().getConfig().TOO_MANY_PLAYERS);
|
||||
BLACKLISTED = Component.text(Sonar.get().getConfig().BLACKLISTED);
|
||||
TOO_FAST_RECONNECT = Component.text(Sonar.get().getConfig().TOO_FAST_RECONNECT);
|
||||
INVALID_USERNAME = Component.text(Sonar.get().getConfig().INVALID_USERNAME);
|
||||
TOO_MANY_ONLINE_PER_IP = Component.text(Sonar.get().getConfig().TOO_MANY_ONLINE_PER_IP);
|
||||
LOCKDOWN_DISCONNECT = Component.text(Sonar.get().getConfig().LOCKDOWN_DISCONNECT);
|
||||
}
|
||||
}
|
||||
|
||||
private static final DummyConnection CLOSED_MINECRAFT_CONNECTION;
|
||||
|
||||
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
||||
@@ -133,14 +110,14 @@ public final class FallbackListener {
|
||||
if (fallback.getBlacklisted().has(inetAddress.toString())) {
|
||||
markConnectionAsDead(mcConnection.getSessionHandler());
|
||||
initialConnection.getConnection().closeWith(Disconnect.create(
|
||||
BLACKLISTED,
|
||||
Sonar.get().getConfig().getBlacklisted(),
|
||||
inboundConnection.getProtocolVersion()
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't continue the verification process if the verification is disabled
|
||||
if (!Sonar.get().getConfig().ENABLE_VERIFICATION) return;
|
||||
if (!Sonar.get().getConfig().isEnableVerification()) return;
|
||||
|
||||
// Check if the player is already verified.
|
||||
// No one wants to be verified over and over again.
|
||||
@@ -162,7 +139,7 @@ public final class FallbackListener {
|
||||
if (fallback.getConnected().containsKey(event.getUsername())
|
||||
|| fallback.getConnected().containsValue(inetAddress)) {
|
||||
initialConnection.getConnection().closeWith(Disconnect.create(
|
||||
ALREADY_VERIFYING,
|
||||
Sonar.get().getConfig().getAlreadyVerifying(),
|
||||
inboundConnection.getProtocolVersion()
|
||||
));
|
||||
return;
|
||||
@@ -170,9 +147,9 @@ public final class FallbackListener {
|
||||
|
||||
// We cannot allow too many players on our Fallback server
|
||||
// There's technically no reason for limiting this, but we'll better stay safe.
|
||||
if (fallback.getConnected().size() > Sonar.get().getConfig().MAXIMUM_VERIFYING_PLAYERS) {
|
||||
if (fallback.getConnected().size() > Sonar.get().getConfig().getMaximumVerifyingPlayers()) {
|
||||
initialConnection.getConnection().closeWith(Disconnect.create(
|
||||
TOO_MANY_PLAYERS,
|
||||
Sonar.get().getConfig().getTooManyPlayers(),
|
||||
inboundConnection.getProtocolVersion()
|
||||
));
|
||||
return;
|
||||
@@ -181,7 +158,7 @@ public final class FallbackListener {
|
||||
// Check if the IP address is currently being rate-limited
|
||||
if (!fallback.getRatelimiter().attempt(inetAddress)) {
|
||||
initialConnection.getConnection().closeWith(Disconnect.create(
|
||||
TOO_FAST_RECONNECT,
|
||||
Sonar.get().getConfig().getTooFastReconnect(),
|
||||
inboundConnection.getProtocolVersion()
|
||||
));
|
||||
return;
|
||||
@@ -190,7 +167,7 @@ public final class FallbackListener {
|
||||
// Check if the player is already queued since we don't want bots to flood the queue
|
||||
if (fallback.getQueue().getQueuedPlayers().containsKey(inetAddress)) {
|
||||
initialConnection.getConnection().closeWith(Disconnect.create(
|
||||
ALREADY_QUEUED,
|
||||
Sonar.get().getConfig().getAlreadyQueued(),
|
||||
inboundConnection.getProtocolVersion()
|
||||
));
|
||||
return;
|
||||
@@ -214,9 +191,8 @@ public final class FallbackListener {
|
||||
|
||||
// Check if the username matches the valid name regex in order to prevent
|
||||
// UTF-16 names or other types of flood attacks
|
||||
if (!Sonar.get().getConfig().VALID_NAME_REGEX
|
||||
.matcher(event.getUsername()).matches()) {
|
||||
mcConnection.closeWith(Disconnect.create(INVALID_USERNAME, mcConnection.getProtocolVersion()));
|
||||
if (!Sonar.get().getConfig().getValidNameRegex().matcher(event.getUsername()).matches()) {
|
||||
mcConnection.closeWith(Disconnect.create(Sonar.get().getConfig().getInvalidUsername(), mcConnection.getProtocolVersion()));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -225,10 +201,7 @@ public final class FallbackListener {
|
||||
pipeline.replace(
|
||||
READ_TIMEOUT,
|
||||
READ_TIMEOUT,
|
||||
new FallbackTimeoutHandler(
|
||||
Sonar.get().getConfig().VERIFICATION_READ_TIMEOUT,
|
||||
TimeUnit.MILLISECONDS
|
||||
)
|
||||
new FallbackTimeoutHandler(Sonar.get().getConfig().getVerificationReadTimeout(), TimeUnit.MILLISECONDS)
|
||||
);
|
||||
|
||||
// We need to determine if the player is premium before we handle the connection,
|
||||
@@ -250,27 +223,22 @@ public final class FallbackListener {
|
||||
public void handle(final @NotNull LoginEvent event) {
|
||||
val connectedPlayer = (ConnectedPlayer) event.getPlayer();
|
||||
|
||||
if (Sonar.get().getConfig().LOCKDOWN_ENABLED) {
|
||||
if (Sonar.get().getConfig().isLockdownEnabled()) {
|
||||
if (!event.getPlayer().hasPermission("sonar.lockdown")) {
|
||||
connectedPlayer.getConnection().closeWith(Disconnect.create(
|
||||
LOCKDOWN_DISCONNECT, connectedPlayer.getProtocolVersion()
|
||||
));
|
||||
Sonar.get().getConfig().getLockdownDisconnect(), connectedPlayer.getProtocolVersion()));
|
||||
|
||||
if (Sonar.get().getConfig().LOCKDOWN_LOG_ATTEMPTS) {
|
||||
Sonar.get().getLogger().info(
|
||||
Sonar.get().getConfig().LOCKDOWN_CONSOLE_LOG
|
||||
.replace("%player%", event.getPlayer().getUsername())
|
||||
.replace("%ip%", Sonar.get().getConfig()
|
||||
.formatAddress(event.getPlayer().getRemoteAddress().getAddress()))
|
||||
.replace("%protocol%",
|
||||
String.valueOf(event.getPlayer().getProtocolVersion().getProtocol()))
|
||||
);
|
||||
if (Sonar.get().getConfig().isLockdownLogAttempts()) {
|
||||
Sonar.get().getLogger().info(Sonar.get().getConfig().getLockdownConsoleLog()
|
||||
.replace("%player%", event.getPlayer().getUsername())
|
||||
.replace("%ip%", Sonar.get().getConfig()
|
||||
.formatAddress(event.getPlayer().getRemoteAddress().getAddress()))
|
||||
.replace("%protocol%",
|
||||
String.valueOf(event.getPlayer().getProtocolVersion().getProtocol())));
|
||||
}
|
||||
return;
|
||||
} else if (Sonar.get().getConfig().LOCKDOWN_ENABLE_NOTIFY) {
|
||||
event.getPlayer().sendMessage(
|
||||
Component.text(Sonar.get().getConfig().LOCKDOWN_NOTIFICATION)
|
||||
);
|
||||
} else if (Sonar.get().getConfig().isLockdownEnableNotify()) {
|
||||
event.getPlayer().sendMessage(Component.text(Sonar.get().getConfig().getLockdownNotification()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +246,7 @@ public final class FallbackListener {
|
||||
|
||||
// Check if the number of online players using the same IP address as
|
||||
// the connecting player is greater than the configured amount
|
||||
final int maxOnlinePerIp = Sonar.get().getConfig().MAXIMUM_ONLINE_PER_IP;
|
||||
final int maxOnlinePerIp = Sonar.get().getConfig().getMaximumOnlinePerIp();
|
||||
|
||||
if (maxOnlinePerIp > 0) {
|
||||
final long onlinePerIp = SonarVelocity.INSTANCE.getPlugin().getServer().getAllPlayers().stream()
|
||||
@@ -288,7 +256,7 @@ public final class FallbackListener {
|
||||
// We use '>=' because the player connecting to the server hasn't joined yet
|
||||
if (onlinePerIp >= maxOnlinePerIp) {
|
||||
connectedPlayer.getConnection().closeWith(Disconnect.create(
|
||||
TOO_MANY_ONLINE_PER_IP, connectedPlayer.getProtocolVersion()
|
||||
Sonar.get().getConfig().getTooManyOnlinePerIp(), connectedPlayer.getProtocolVersion()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -122,11 +122,11 @@ public final class FallbackSessionHandler implements MinecraftSessionHandler {
|
||||
// The player joined the verification
|
||||
Statistics.REAL_TRAFFIC.increment();
|
||||
|
||||
if (Sonar.get().getConfig().LOG_CONNECTIONS) {
|
||||
if (Sonar.get().getConfig().isLogConnections()) {
|
||||
// Only log the processing message if the server isn't under attack.
|
||||
// We let the user override this through the configuration.
|
||||
if (!fallback.isPotentiallyUnderAttack() || Sonar.get().getConfig().LOG_DURING_ATTACK) {
|
||||
fallback.getLogger().info(Sonar.get().getConfig().VERIFICATION_CONNECT_LOG
|
||||
if (!fallback.isPotentiallyUnderAttack() || Sonar.get().getConfig().isLogDuringAttack()) {
|
||||
fallback.getLogger().info(Sonar.get().getConfig().getVerificationConnectLog()
|
||||
.replace("%name%", connectedPlayer.getUsername())
|
||||
.replace("%ip%", Sonar.get().getConfig().formatAddress(fallbackPlayer.getInetAddress()))
|
||||
.replace("%protocol%", String.valueOf(fallbackPlayer.getProtocolVersion().getProtocol())));
|
||||
|
||||
+2
-5
@@ -19,7 +19,6 @@ package xyz.jonesdev.sonar.velocity.fallback;
|
||||
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -49,10 +48,8 @@ public final class FallbackUserWrapper implements FallbackUser<ConnectedPlayer,
|
||||
private final ProtocolVersion protocolVersion;
|
||||
|
||||
@Override
|
||||
public void disconnect(final @NotNull String reason) {
|
||||
final String serialized = ProtocolUtils.getJsonChatSerializer(connection.getProtocolVersion())
|
||||
.serialize(Component.text(reason));
|
||||
connection.closeWith(Disconnect.create(serialized));
|
||||
public void disconnect(final @NotNull Component reason) {
|
||||
connection.closeWith(Disconnect.create(reason));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"id": "sonar",
|
||||
"name": "$name",
|
||||
"version": "$version",
|
||||
"description": "$description",
|
||||
"url": "$url",
|
||||
"authors": [
|
||||
"Jones Development",
|
||||
"Sonar Contributors"
|
||||
],
|
||||
"dependencies": [],
|
||||
"main": "$main"
|
||||
}
|
||||
Reference in new issue
Block a user