mirror of
https://github.com/jonesdevelopment/sonar.git
synced 2026-11-09 02:45:19 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c73ecf60f | ||
|
|
c116d7b556 | ||
|
|
057cda6c7a | ||
|
|
3a97a1f949 | ||
|
|
69b20769de | ||
|
|
9b87fde7c6 | ||
|
|
b939767ab9 | ||
|
|
d5a8cd5346 | ||
|
|
aa4e4662bb | ||
|
|
29187d4248 |
No files matched your search
+49
-57
@@ -31,6 +31,7 @@ import org.jetbrains.annotations.Range;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.config.SonarConfiguration;
|
||||
import xyz.jonesdev.sonar.api.database.model.VerifiedPlayer;
|
||||
import xyz.jonesdev.sonar.api.fingerprint.FingerprintingUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.SQLException;
|
||||
@@ -45,8 +46,6 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public final class VerifiedPlayerController {
|
||||
private static final ExecutorService DB_UPDATE_SERVICE = Executors.newSingleThreadExecutor();
|
||||
|
||||
@Getter
|
||||
private final Set<String> cache = Collections.synchronizedSet(new HashSet<>());
|
||||
private @Nullable ConnectionSource connectionSource;
|
||||
@@ -54,6 +53,7 @@ public final class VerifiedPlayerController {
|
||||
private QueryBuilder<VerifiedPlayer, Integer> queryBuilder;
|
||||
@Getter
|
||||
private final @NotNull SonarConfiguration.Database.Type cachedDatabaseType;
|
||||
private final ExecutorService updateService = Executors.newSingleThreadExecutor();
|
||||
|
||||
public VerifiedPlayerController(final @NotNull LibraryManager libraryManager) {
|
||||
final SonarConfiguration.Database database = Sonar.get().getConfig().getDatabase();
|
||||
@@ -95,7 +95,6 @@ public final class VerifiedPlayerController {
|
||||
Sonar.get().getConfig().getGeneralConfig().getString("database.password"),
|
||||
cachedDatabaseType.getDatabaseType());
|
||||
|
||||
// Create database table
|
||||
try {
|
||||
TableUtils.createTableIfNotExists(connectionSource, VerifiedPlayer.class);
|
||||
} catch (SQLException ignored) {
|
||||
@@ -111,14 +110,16 @@ public final class VerifiedPlayerController {
|
||||
|
||||
// Make sure to run the clean task and the caching task in the same thread
|
||||
// https://github.com/jonesdevelopment/sonar/issues/150
|
||||
DB_UPDATE_SERVICE.execute(() -> {
|
||||
try {
|
||||
// Make sure to clear all outdated entries first
|
||||
clearOld(database.getMaximumAge());
|
||||
// Add all entries from the database to the cache
|
||||
dao.queryForAll().forEach(verifiedPlayer -> cache.add(verifiedPlayer.getFingerprint()));
|
||||
} catch (SQLException exception) {
|
||||
exception.printStackTrace(System.err);
|
||||
updateService.execute(() -> {
|
||||
if (connectionSource != null) {
|
||||
try {
|
||||
// Make sure to clear all outdated entries first
|
||||
clearOld(database.getMaximumAge());
|
||||
// Add all entries from the database to the cache
|
||||
dao.queryForAll().forEach(verifiedPlayer -> cache.add(verifiedPlayer.getFingerprint()));
|
||||
} catch (SQLException exception) {
|
||||
exception.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (SQLException exception) {
|
||||
@@ -137,16 +138,17 @@ public final class VerifiedPlayerController {
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace(System.err);
|
||||
}
|
||||
// Make sure not to reuse a closed connection
|
||||
connectionSource = null;
|
||||
updateService.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all old entries using the given timestamp.
|
||||
* Clear all old entries using the given timestamp
|
||||
*/
|
||||
private void clearOld(final @Range(from = 1, to = 365) int maximumAge) throws SQLException {
|
||||
final long timestamp = Instant.now()
|
||||
.minus(maximumAge, ChronoUnit.DAYS)
|
||||
.getEpochSecond() * 1000L; // convert to ms
|
||||
final long timestamp = Instant.now().minus(maximumAge, ChronoUnit.DAYS).getEpochSecond() * 1000L;
|
||||
|
||||
final List<VerifiedPlayer> oldEntries = queryBuilder.where()
|
||||
.lt("timestamp", new Timestamp(timestamp))
|
||||
@@ -175,34 +177,36 @@ public final class VerifiedPlayerController {
|
||||
return;
|
||||
}
|
||||
|
||||
DB_UPDATE_SERVICE.execute(() -> {
|
||||
// We cannot throw a NullPointerException within the executor service
|
||||
// because we want to handle the error instead of simply throwing an exception
|
||||
if (connectionSource == null) {
|
||||
return;
|
||||
}
|
||||
updateService.execute(() -> {
|
||||
if (connectionSource != null) {
|
||||
try {
|
||||
final List<VerifiedPlayer> verifiedPlayer = queryBuilder.where()
|
||||
.eq("fingerprint", fingerprint)
|
||||
.query();
|
||||
|
||||
try {
|
||||
final List<VerifiedPlayer> verifiedPlayer = queryBuilder.where()
|
||||
.eq("fingerprint", fingerprint)
|
||||
.query();
|
||||
|
||||
if (verifiedPlayer != null) {
|
||||
for (final VerifiedPlayer player : verifiedPlayer) {
|
||||
dao.delete(player);
|
||||
if (verifiedPlayer != null) {
|
||||
for (final VerifiedPlayer player : verifiedPlayer) {
|
||||
dao.delete(player);
|
||||
}
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
exception.printStackTrace(System.err);
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
exception.printStackTrace(System.err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* First, cache the player locally and then,
|
||||
* secondly, asynchronously add the player to the database.
|
||||
*
|
||||
* @param player VerifiedPlayer model
|
||||
* Creates a new VerifiedPlayer model from the given username, host address, and timestamp
|
||||
*/
|
||||
public void add(final @NotNull String username, final @NotNull String hostAddress, final long timestamp) {
|
||||
final String fingerprint = FingerprintingUtil.getFingerprint(username, hostAddress);
|
||||
// Add a new VerifiedPlayer object from the given parameters to the database
|
||||
add(new VerifiedPlayer(fingerprint, timestamp));
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches the player locally and then adds the player to the database
|
||||
*/
|
||||
public void add(final @NotNull VerifiedPlayer player) {
|
||||
cache.add(player.getFingerprint());
|
||||
@@ -212,39 +216,27 @@ public final class VerifiedPlayerController {
|
||||
return;
|
||||
}
|
||||
|
||||
DB_UPDATE_SERVICE.execute(() -> {
|
||||
// We cannot throw a NullPointerException within the executor service
|
||||
// because we want to handle the error instead of simply throwing an exception
|
||||
if (connectionSource == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
dao.createIfNotExists(player);
|
||||
} catch (SQLException exception) {
|
||||
exception.printStackTrace(System.err);
|
||||
updateService.execute(() -> {
|
||||
if (connectionSource != null) {
|
||||
try {
|
||||
dao.createIfNotExists(player);
|
||||
} catch (SQLException exception) {
|
||||
exception.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of verified IP addresses
|
||||
*
|
||||
* @return Estimated size of the local cache
|
||||
*/
|
||||
public int estimatedSize() {
|
||||
return cache.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the local cache, and, if the database type is set,
|
||||
* remove the table from the database.
|
||||
*/
|
||||
public synchronized void clearAll() {
|
||||
public void clearAll() {
|
||||
cache.clear();
|
||||
|
||||
// Delete the entire table from the database, if necessary
|
||||
if (cachedDatabaseType != SonarConfiguration.Database.Type.NONE) {
|
||||
if (connectionSource != null
|
||||
&& cachedDatabaseType != SonarConfiguration.Database.Type.NONE) {
|
||||
try {
|
||||
dao.deleteBuilder().delete();
|
||||
} catch (SQLException exception) {
|
||||
|
||||
@@ -66,7 +66,7 @@ public final class AttackTracker {
|
||||
// An attack has been detected
|
||||
if (currentAttack == null) {
|
||||
currentAttack = new AttackStatistics();
|
||||
currentAttack.successfulVerifications = Sonar.get().getVerifiedPlayerController().estimatedSize();
|
||||
currentAttack.successfulVerifications = Sonar.get().getVerifiedPlayerController().getCache().size();
|
||||
currentAttack.failedVerifications = Sonar.get().getStatistics().getTotalFailedVerifications();
|
||||
Sonar.get().getEventManager().publish(new AttackDetectedEvent());
|
||||
Sonar.get().getChatNotificationHandler().handleNotification();
|
||||
@@ -118,7 +118,7 @@ public final class AttackTracker {
|
||||
final String endTimestamp = String.valueOf(System.currentTimeMillis() / 1000L);
|
||||
final long blacklisted = Sonar.get().getFallback().getBlacklist().estimatedSize();
|
||||
// Calculate during-attack-statistics using their deltas
|
||||
final long totalVerified = Sonar.get().getVerifiedPlayerController().estimatedSize();
|
||||
final long totalVerified = Sonar.get().getVerifiedPlayerController().getCache().size();
|
||||
final long verified = Math.max(totalVerified - currentAttack.successfulVerifications, 0);
|
||||
final long totalFailed = Sonar.get().getStatistics().getTotalFailedVerifications();
|
||||
final long failed = Math.max(totalFailed - currentAttack.failedVerifications, 0);
|
||||
|
||||
@@ -27,10 +27,9 @@ import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.api.command.SonarCommand;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
public final class BukkitSonarCommand implements CommandExecutor, TabExecutor, SonarCommand {
|
||||
|
||||
@Override
|
||||
@@ -55,7 +54,7 @@ public final class BukkitSonarCommand implements CommandExecutor, TabExecutor, S
|
||||
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();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return getCachedTabSuggestions(args);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.api.command.SonarCommand;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import java.util.Collections;
|
||||
|
||||
public final class BungeeSonarCommand extends Command implements TabExecutor, SonarCommand {
|
||||
public BungeeSonarCommand() {
|
||||
@@ -48,7 +48,7 @@ public final class BungeeSonarCommand extends Command implements TabExecutor, So
|
||||
public Iterable<String> onTabComplete(final @NotNull 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();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return getCachedTabSuggestions(args);
|
||||
}
|
||||
|
||||
@@ -38,10 +38,6 @@ import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
import static java.awt.image.BufferedImage.TYPE_3BYTE_BGR;
|
||||
import static xyz.jonesdev.sonar.captcha.StandardTTFFontProvider.FONTS;
|
||||
import static xyz.jonesdev.sonar.captcha.StandardTTFFontProvider.STANDARD_FONT_SIZE;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public final class StandardCaptchaGenerator implements CaptchaGenerator {
|
||||
@@ -92,7 +88,7 @@ public final class StandardCaptchaGenerator implements CaptchaGenerator {
|
||||
|
||||
private @NotNull BufferedImage createBackgroundImage() {
|
||||
// Create a new image buffer with a 3-byte color spectrum
|
||||
final BufferedImage image = new BufferedImage(width, height, TYPE_3BYTE_BGR);
|
||||
final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
|
||||
if (background == null) {
|
||||
// Fill the entire image with a noise texture
|
||||
return FBM.filter(image, image);
|
||||
@@ -128,7 +124,7 @@ public final class StandardCaptchaGenerator implements CaptchaGenerator {
|
||||
|
||||
for (int i = 0; i < answer.length; i++) {
|
||||
// Create a glyph vector for the character with a random font
|
||||
final Font font = FONTS[RANDOM.nextInt(FONTS.length)];
|
||||
final Font font = StandardTTFFontProvider.FONTS[RANDOM.nextInt(StandardTTFFontProvider.FONTS.length)];
|
||||
glyphs[i] = font.createGlyphVector(ctx, String.valueOf(answer[i]));
|
||||
}
|
||||
|
||||
@@ -139,7 +135,7 @@ public final class StandardCaptchaGenerator implements CaptchaGenerator {
|
||||
.mapToDouble(glyph -> glyph.getLogicalBounds().getWidth() * scalingXY - 1)
|
||||
.sum();
|
||||
double beginX = Math.max(Math.min(width / 2D - totalWidth / 2D, totalWidth), 0);
|
||||
double beginY = (height + STANDARD_FONT_SIZE / 2D) / 2D + scalingXY;
|
||||
double beginY = (height + StandardTTFFontProvider.STANDARD_FONT_SIZE / 2D) / 2D + scalingXY;
|
||||
|
||||
// Draw each glyph one by one
|
||||
for (final GlyphVector glyph : glyphs) {
|
||||
|
||||
+2
-2
@@ -21,12 +21,12 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import io.netty.util.ByteProcessor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.common.util.exception.QuietDecoderException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static io.netty.util.ByteProcessor.FIND_NON_NUL;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.DEBUG;
|
||||
|
||||
// https://github.com/PaperMC/Velocity/blob/dev/3.0.0/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java
|
||||
@@ -42,7 +42,7 @@ public final class FallbackVarInt21FrameDecoder extends ByteToMessageDecoder {
|
||||
}
|
||||
|
||||
// Skip any runs of 0x00 we might find
|
||||
final int packetStart = byteBuf.forEachByte(FIND_NON_NUL);
|
||||
final int packetStart = byteBuf.forEachByte(ByteProcessor.FIND_NON_NUL);
|
||||
if (packetStart == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
+2
-2
@@ -184,10 +184,10 @@ public class FallbackPreparer {
|
||||
teleportMinecart = new TeleportEntityPacket(
|
||||
VEHICLE_ENTITY_ID, SPAWN_X_POSITION, IN_VOID_Y_POSITION, SPAWN_Z_POSITION, false);
|
||||
spawnBoatEntity = new FallbackPacketSnapshot(new SpawnEntityPacket(VEHICLE_ENTITY_ID,
|
||||
EntityType.BOAT, RANDOM.nextInt(16), IN_AIR_Y_POSITION, RANDOM.nextInt(16),
|
||||
EntityType.BOAT, SPAWN_X_POSITION, IN_AIR_Y_POSITION, SPAWN_Z_POSITION,
|
||||
0, 0, 0, 0));
|
||||
spawnMinecartEntity = new FallbackPacketSnapshot(new SpawnEntityPacket(VEHICLE_ENTITY_ID,
|
||||
EntityType.MINECART, RANDOM.nextInt(16), IN_AIR_Y_POSITION, RANDOM.nextInt(16),
|
||||
EntityType.MINECART, SPAWN_X_POSITION, IN_AIR_Y_POSITION, SPAWN_Z_POSITION,
|
||||
0, 0, 0, 0));
|
||||
setPassengers = new FallbackPacketSnapshot(new SetPassengersPacket(VEHICLE_ENTITY_ID, PLAYER_ENTITY_ID));
|
||||
|
||||
|
||||
+2
@@ -126,6 +126,8 @@ public final class FallbackPreJoinHandler extends FallbackVerificationHandler {
|
||||
} else if (packet instanceof ClientInformationPacket) {
|
||||
final ClientInformationPacket clientInformation = (ClientInformationPacket) packet;
|
||||
|
||||
checkState(clientInformation.getViewDistance() >= 2,
|
||||
"view distance: " + clientInformation.getViewDistance());
|
||||
// Ensure that the client locale is correct
|
||||
validateClientLocale(clientInformation.getLocale());
|
||||
// Check if the player sent an unused bit flag in the skin section
|
||||
|
||||
+1
-1
@@ -130,7 +130,7 @@ public final class GlobalSonarStatistics implements SonarStatistics {
|
||||
|
||||
@Override
|
||||
public int getTotalPlayersVerified() {
|
||||
return Sonar.get().getVerifiedPlayerController().estimatedSize();
|
||||
return Sonar.get().getVerifiedPlayerController().getCache().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -70,7 +70,7 @@ public final class StatisticsCommand extends Subcommand {
|
||||
|
||||
placeholders = new TagResolver.Single[]{
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix()),
|
||||
Placeholder.unparsed("verified", DECIMAL_FORMAT.format(Sonar.get().getVerifiedPlayerController().estimatedSize())),
|
||||
Placeholder.unparsed("verified", DECIMAL_FORMAT.format(Sonar.get().getVerifiedPlayerController().getCache().size())),
|
||||
Placeholder.unparsed("verifying", DECIMAL_FORMAT.format(Sonar.get().getFallback().getConnected().size())),
|
||||
Placeholder.unparsed("blacklisted", DECIMAL_FORMAT.format(Sonar.get().getFallback().getBlacklist().estimatedSize())),
|
||||
Placeholder.unparsed("queued", DECIMAL_FORMAT.format(Sonar.get().getFallback().getQueue().getPlayers().size())),
|
||||
|
||||
@@ -95,7 +95,7 @@ public final class VerifiedCommand extends Subcommand {
|
||||
}
|
||||
|
||||
case "clear": {
|
||||
final int verifiedSize = Sonar.get().getVerifiedPlayerController().estimatedSize();
|
||||
final int verifiedSize = Sonar.get().getVerifiedPlayerController().getCache().size();
|
||||
|
||||
if (verifiedSize == 0) {
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
@@ -113,7 +113,7 @@ public final class VerifiedCommand extends Subcommand {
|
||||
}
|
||||
|
||||
case "size": {
|
||||
final int verifiedSize = Sonar.get().getVerifiedPlayerController().estimatedSize();
|
||||
final int verifiedSize = Sonar.get().getVerifiedPlayerController().getCache().size();
|
||||
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.verified.size"),
|
||||
|
||||
@@ -199,6 +199,7 @@ verification:
|
||||
incorrect: '<prefix><red>Zadali jste špatnou odpověď! Zkuste to prosím znovu.'
|
||||
|
||||
# Zpráva zobrazená hráči při ověřování
|
||||
# Nastavením této hodnoty na '' uvítací zprávu zcela vypnete
|
||||
welcome: '<prefix><green>Jste ověřováni. Prosím, nehýbejte se; jedná se o automatický proces.'
|
||||
|
||||
# Zpráva o odpojení, která se zobrazí, když se někdo připojí, zatímco captcha ještě není připravena
|
||||
|
||||
@@ -200,6 +200,7 @@ verification:
|
||||
incorrect: '<prefix><red>Du hast eine falsche Antwort eingegeben! Bitte versuche es erneut.'
|
||||
|
||||
# Nachricht, die dem Spieler angezeigt wird, wenn er überprüft wird
|
||||
# Setzen Sie dies auf '', um die Willkommensnachricht vollständig zu deaktivieren
|
||||
welcome: '<prefix><green>Sie werden gerade überprüft. Bitte bewegen Sie sich nicht, dies ist ein automatischer Vorgang.'
|
||||
|
||||
# Trennungs-Nachricht, die angezeigt wird, wenn jemand beitritt, während der CAPTCHA noch nicht vorbereitet wurde
|
||||
|
||||
@@ -199,6 +199,7 @@ verification:
|
||||
incorrect: '<prefix><red>You have entered a wrong answer! Please try again.'
|
||||
|
||||
# Message shown to the player when they are being verified
|
||||
# Set this to '' to disable the welcome message entirely
|
||||
welcome: '<prefix><green>You are being verified. Please do not move; this is an automatic process.'
|
||||
|
||||
# Disconnect message that is shown when someone joins while the captcha hasn't been prepared yet
|
||||
|
||||
@@ -199,7 +199,8 @@ verification:
|
||||
incorrect: '<prefix><red>¡Has introducido una respuesta incorrecta! Por favor, inténtalo de nuevo.'
|
||||
|
||||
# Mensaje que se muestra al jugador cuando está siendo verificado
|
||||
welcome: '<prefijo><verde>Estás siendo verificado. Por favor, no te muevas; se trata de un proceso automático.'
|
||||
# Ajústalo a '' para desactivar por completo el mensaje de bienvenida
|
||||
welcome: '<prefix><green>Estás siendo verificado. Por favor, no te muevas; se trata de un proceso automático.'
|
||||
|
||||
# Disconnect message that is shown when someone joins while the captcha hasn't been prepared yet
|
||||
currently-preparing:
|
||||
|
||||
@@ -199,6 +199,7 @@ verification:
|
||||
incorrect: '<prefix><red>Vous avez saisi une mauvaise réponse ! Veuillez réessayer.'
|
||||
|
||||
# Message affiché au joueur lorsqu'il est en cours de vérification
|
||||
# Réglez ce paramètre sur '' pour désactiver complètement le message de bienvenue
|
||||
welcome: '<prefix><green>Vous êtes en cours de vérification. Ne bougez pas, il s''agit d''un processus automatique.'
|
||||
|
||||
# Message de déconnexion affiché lorsque quelqu'un rejoint alors que le CAPTCHA n'a pas été préparé
|
||||
|
||||
@@ -199,6 +199,7 @@ verification:
|
||||
incorrect: '<prefix><red>Anda telah memasukkan jawaban yang salah! Silakan coba lagi.'
|
||||
|
||||
# Pesan yang ditampilkan kepada pemain ketika mereka sedang diverifikasi
|
||||
# Atur ini ke '' untuk menonaktifkan pesan selamat datang sepenuhnya
|
||||
welcome: '<prefix><green>Anda sedang diverifikasi. Jangan bergerak; ini adalah proses otomatis.'
|
||||
|
||||
# Pesan putus yang ditampilkan saat seseorang bergabung sementara captcha belum disiapkan
|
||||
|
||||
@@ -199,6 +199,7 @@ verification:
|
||||
incorrect: '<prefix><red>თქვენ მიუთითეთ არასწორი კოდი. გთხოვთ ხელახლა ცადოთ.'
|
||||
|
||||
# Message shown to the player when they are being verified
|
||||
# Set this to '' to disable the welcome message entirely
|
||||
welcome: '<prefix><green>You are being verified. Please do not move; this is an automatic process.'
|
||||
|
||||
# გავარდნის მესიჯი რომელიც არის ნაჩვენები როცა ვინმე შემოვა მაგრამ CAPTCHA არ არის მზად
|
||||
|
||||
@@ -199,6 +199,7 @@ verification:
|
||||
incorrect: '<prefix><red>Je hebt een verkeerd antwoord ingevoerd! Probeer het opnieuw.'
|
||||
|
||||
# Bericht dat aan de speler wordt getoond als hij wordt geverifieerd
|
||||
# Stel dit in op '' om het welkomstbericht volledig uit te schakelen
|
||||
welcome: '<prefix><green>U wordt geverifieerd. Beweeg je niet, dit is een automatisch proces.'
|
||||
|
||||
# Verbindingsbericht dat wordt getoond als iemand zich aansluit terwijl de captcha nog niet is voorbereid
|
||||
|
||||
@@ -202,6 +202,7 @@ verification:
|
||||
incorrect: '<prefix><red>Wprowadzono nieprawidłowy kod. Spróbuj ponownie.'
|
||||
|
||||
# Wiadomość wyświetlana graczowi podczas weryfikacji
|
||||
# Ustaw wartość '', aby całkowicie wyłączyć wiadomość powitalną
|
||||
welcome: '<prefix><green>Jesteś w trakcie weryfikacji. Nie ruszaj się, to proces automatyczny.'
|
||||
|
||||
# Disconnect message that is shown when someone joins while the captcha hasn't been prepared yet
|
||||
|
||||
@@ -199,6 +199,7 @@ verification:
|
||||
incorrect: '<prefix><red>Вы ввели неправильный ответ! Пожалуйста, попробуйте еще раз.'
|
||||
|
||||
# Сообщение, показываемое игроку при прохождении верификации
|
||||
# Установите значение '', чтобы полностью отключить приветственное сообщение
|
||||
welcome: '<prefix><green>Вы проходите верификацию. Пожалуйста, не двигайтесь, это автоматический процесс.'
|
||||
|
||||
# Сообщение, которое показывается когда капча еще не подготовлена
|
||||
|
||||
@@ -199,6 +199,7 @@ verification:
|
||||
incorrect: '<prefix><red>Yanlış bir cevap girdiniz! Lütfen tekrar deneyin.'
|
||||
|
||||
# Doğrulandıklarında oyuncuya gösterilen mesaj
|
||||
# Karşılama mesajını tamamen devre dışı bırakmak için bunu '' olarak ayarlayın
|
||||
welcome: '<prefix><green>Doğrulanıyorsunuz. Lütfen hareket etmeyin; bu otomatik bir süreçtir.'
|
||||
|
||||
# CAPTCHA henüz hazırlanmamışken birisi katıldığında gösterilen bağlantıyı kesme mesajı
|
||||
|
||||
@@ -199,6 +199,7 @@ verification:
|
||||
incorrect: '<prefix><red>您输入了错误的答案! 请重试.'
|
||||
|
||||
# 当玩家正在接受验证时显示的信息
|
||||
# 将此项设置为''可完全禁用欢迎信息
|
||||
welcome: '<prefix><green>您正在接受验证. 请不要移动; 这是一个自动过程.'
|
||||
|
||||
# 当验证码还在准备的时候显示的踢出消息
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
group=xyz.jonesdev
|
||||
version=2.1.9
|
||||
version=2.1.10
|
||||
description=A lightweight, effective, and easy-to-use anti-bot plugin for Velocity, BungeeCord, and Bukkit.
|
||||
@@ -28,7 +28,7 @@ bstats-bukkit = { module = "org.bstats:bstats-bukkit", version.ref = "bstats" }
|
||||
bstats-velocity = { module = "org.bstats:bstats-velocity", version.ref = "bstats" }
|
||||
imagefilters = "com.jhlabs:filters:2.0.235-1"
|
||||
caffeine = "com.github.ben-manes.caffeine:caffeine:3.1.8"
|
||||
annotations = "org.jetbrains:annotations:24.1.0"
|
||||
annotations = "org.jetbrains:annotations:25.0.0"
|
||||
simpleyaml = "com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4"
|
||||
ormlite = "com.j256.ormlite:ormlite-jdbc:6.1"
|
||||
lombok = "org.projectlombok:lombok:1.18.34"
|
||||
|
||||
+2
-3
@@ -23,10 +23,9 @@ import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.api.command.SonarCommand;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
public final class VelocitySonarCommand implements SimpleCommand, SonarCommand {
|
||||
|
||||
@Override
|
||||
@@ -44,7 +43,7 @@ public final class VelocitySonarCommand implements SimpleCommand, SonarCommand {
|
||||
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();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return getCachedTabSuggestions(invocation.arguments());
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user