mirror of
https://github.com/jonesdevelopment/sonar.git
synced 2026-11-05 09:20:15 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f618d1ce77 | ||
|
|
ea35904d33 | ||
|
|
ececb66bf1 | ||
|
|
6caa37e3e0 | ||
|
|
a057199e0b | ||
|
|
d0bbcdc600 | ||
|
|
0a5ce856f3 | ||
|
|
261acbd9e2 |
No files matched your search
+1
-1
@@ -1,3 +1,3 @@
|
||||
group=xyz.jonesdev.sonar
|
||||
version=2.0.28
|
||||
version=2.0.29
|
||||
description=Effective Anti-bot plugin for Velocity, BungeeCord, and Bukkit (1.7-latest)
|
||||
@@ -182,20 +182,31 @@ public final class SonarConfiguration {
|
||||
verification.vehicle.timing = Verification.Timing.valueOf(generalConfig.getString("verification.checks.vehicle.timing"));
|
||||
|
||||
verification.map.timing = Verification.Timing.valueOf(generalConfig.getString("verification.checks.map-captcha.timing"));
|
||||
verification.map.flare = generalConfig.getBoolean("verification.checks.map-captcha.effects.flare");
|
||||
verification.map.scratches = generalConfig.getBoolean("verification.checks.map-captcha.effects.scratches");
|
||||
verification.map.ripple = generalConfig.getBoolean("verification.checks.map-captcha.effects.ripple");
|
||||
verification.map.smear = generalConfig.getBoolean("verification.checks.map-captcha.effects.smear");
|
||||
verification.map.pinch = generalConfig.getBoolean("verification.checks.map-captcha.effects.pinch");
|
||||
verification.map.saturation = (float) generalConfig.getYaml().getDouble(
|
||||
"verification.checks.map-captcha.effects.saturation", 0.3);
|
||||
verification.map.distortion = (float) generalConfig.getYaml().getDouble(
|
||||
"verification.checks.map-captcha.effects.distortion", 2);
|
||||
verification.map.precomputeAmount = generalConfig.getInt("verification.checks.map-captcha.precompute");
|
||||
verification.map.maxDuration = generalConfig.getInt("verification.checks.map-captcha.max-duration");
|
||||
verification.map.bump = generalConfig.getBoolean("verification.checks.map-captcha.effects.bump");
|
||||
|
||||
verification.map.distortion.enabled = generalConfig.getBoolean("verification.checks.map-captcha.effects.distortion.enabled");
|
||||
verification.map.distortion.shape = generalConfig.getInt("verification.checks.map-captcha.effects.distortion.shape");
|
||||
verification.map.distortion.distance = generalConfig.getInt("verification.checks.map-captcha.effects.distortion.distance");
|
||||
verification.map.distortion.density = (float) generalConfig.getYaml().getDouble("verification.checks.map-captcha.effects.distortion.density");
|
||||
verification.map.distortion.mix = (float) generalConfig.getYaml().getDouble("verification.checks.map-captcha.effects.distortion.mix");
|
||||
|
||||
final String backgroundImagePath = generalConfig.getString("verification.checks.map-captcha.background");
|
||||
if (!backgroundImagePath.isEmpty()) {
|
||||
verification.map.backgroundImage = new File(pluginFolder, backgroundImagePath);
|
||||
if (!verification.map.backgroundImage.exists()) {
|
||||
Sonar.get().getLogger().error("The background image does not exist! Please check the configuration.");
|
||||
}
|
||||
} else {
|
||||
verification.map.backgroundImage = null;
|
||||
}
|
||||
|
||||
verification.map.autoColor = generalConfig.getBoolean("verification.checks.map-captcha.auto-color");
|
||||
verification.map.precomputeAmount = clamp(generalConfig.getInt("verification.checks.map-captcha.precompute"), 1, 1000);
|
||||
verification.map.maxDuration = clamp(generalConfig.getInt("verification.checks.map-captcha.max-duration"), 5000, 360000);
|
||||
verification.map.maxTries = generalConfig.getInt("verification.checks.map-captcha.max-tries");
|
||||
verification.map.dictionary = generalConfig.getString("verification.checks.map-captcha.dictionary");
|
||||
verification.map.fonts = generalConfig.getStringList("verification.checks.map-captcha.font-names");
|
||||
|
||||
verification.brand.enabled = generalConfig.getBoolean("verification.checks.client-brand.enabled");
|
||||
verification.brand.validRegex = Pattern.compile(generalConfig.getString("verification.checks.client-brand.valid-regex"));
|
||||
@@ -317,7 +328,6 @@ public final class SonarConfiguration {
|
||||
verification.successLog = formatString(messagesConfig.getString("verification.logs.successful"));
|
||||
|
||||
verification.map.enterCode = deserialize(formatString(messagesConfig.getString("verification.captcha.enter-code")));
|
||||
verification.map.enterCodeActionBar = formatString(messagesConfig.getString("verification.captcha.action-bar"));
|
||||
verification.map.failedCaptcha = deserialize(formatString(messagesConfig.getString("verification.captcha.incorrect")));
|
||||
verification.currentlyPreparing = deserialize(fromList(messagesConfig.getStringList("verification.currently-preparing")));
|
||||
verification.tooFastReconnect = deserialize(fromList(messagesConfig.getStringList("verification.too-fast-reconnect")));
|
||||
@@ -352,7 +362,11 @@ public final class SonarConfiguration {
|
||||
}
|
||||
|
||||
private static int clamp(final int v, final int max, final int min) {
|
||||
return Math.max(Math.min(v, min), max);
|
||||
final int output = Math.max(Math.min(v, min), max);
|
||||
if (output != v) {
|
||||
Sonar.get().getLogger().warn("Clamped configuration value {} to {}", v, output);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public String formatAddress(final InetAddress inetAddress) {
|
||||
@@ -457,21 +471,28 @@ public final class SonarConfiguration {
|
||||
@Getter
|
||||
public static final class Map {
|
||||
private Timing timing;
|
||||
private boolean flare;
|
||||
private boolean scratches;
|
||||
private boolean ripple;
|
||||
private boolean smear;
|
||||
private boolean pinch;
|
||||
private float saturation;
|
||||
private float distortion;
|
||||
private boolean bump;
|
||||
private File backgroundImage;
|
||||
private boolean autoColor;
|
||||
private int precomputeAmount;
|
||||
private int maxDuration;
|
||||
private int maxTries;
|
||||
private String dictionary;
|
||||
private Component enterCode;
|
||||
private Component failedCaptcha;
|
||||
private String enterCodeActionBar;
|
||||
private List<String> fonts;
|
||||
|
||||
private Smear distortion = new Smear();
|
||||
|
||||
@Getter
|
||||
public static final class Smear {
|
||||
private boolean enabled;
|
||||
private int shape;
|
||||
private int distance;
|
||||
private float density;
|
||||
private float mix;
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
|
||||
@@ -13,7 +13,8 @@ bukkit {
|
||||
authors = listOf("Jones Development", "Sonar Contributors")
|
||||
website = "https://jonesdev.xyz/discord/"
|
||||
load = BukkitPluginDescription.PluginLoadOrder.POSTWORLD
|
||||
softDepend = listOf("Geyser-Spigot", "floodgate", "Protocolize", "packetevents", "ProtocolLib", "ViaVersion")
|
||||
softDepend = listOf("Geyser-Spigot", "floodgate", "Protocolize", "ProtocolSupport",
|
||||
"ViaVersion", "packetevents", "ProtocolLib", "FastLogin")
|
||||
foliaSupported = true
|
||||
|
||||
commands {
|
||||
|
||||
@@ -9,7 +9,7 @@ bungee {
|
||||
version = rootProject.version.toString().split("-")[0]
|
||||
main = "xyz.jonesdev.sonar.bungee.SonarBungeePlugin"
|
||||
author = "Jones Development, Sonar Contributors"
|
||||
softDepends = setOf("Geyser-BungeeCord", "floodgate", "Protocolize", "ViaVersion")
|
||||
softDepends = setOf("Geyser-BungeeCord", "floodgate", "Protocolize", "ViaVersion", "packetevents", "FastLogin")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -5,7 +5,7 @@ dependencies {
|
||||
compileOnly("net.kyori:adventure-nbt:4.16.0")
|
||||
|
||||
// CAPTCHA image generation
|
||||
implementation("xyz.jonesdev.capja:capja:0.1.2")
|
||||
implementation("xyz.jonesdev:capja:1.0.2")
|
||||
}
|
||||
|
||||
java.sourceCompatibility = JavaVersion.VERSION_11
|
||||
|
||||
@@ -136,6 +136,7 @@ public abstract class SonarBootstrap<T> implements Sonar {
|
||||
}
|
||||
|
||||
// Prepare cached packets
|
||||
getLogger().info("Taking cached snapshots of all packets...");
|
||||
FallbackPreparer.prepare();
|
||||
|
||||
// Update ratelimiter caches
|
||||
|
||||
+2
-2
@@ -30,7 +30,7 @@ import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPreparer;
|
||||
import xyz.jonesdev.sonar.common.statistics.GlobalSonarStatistics;
|
||||
import xyz.jonesdev.sonar.common.utility.geyser.GeyserUtil;
|
||||
import xyz.jonesdev.sonar.common.util.GeyserDetection;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
@@ -154,7 +154,7 @@ public class FallbackChannelHandlerAdapter extends ChannelInboundHandlerAdapter
|
||||
}
|
||||
|
||||
// Completely skip Geyser connections if configured
|
||||
final boolean geyser = GeyserUtil.isGeyserConnection(channel, socketAddress);
|
||||
final boolean geyser = GeyserDetection.isGeyserConnection(channel, socketAddress);
|
||||
if (geyser && !Sonar.get().getConfig().getVerification().isCheckGeyser()) {
|
||||
initialLogin(ctx, loginPacket, encoder, handler);
|
||||
return;
|
||||
|
||||
+5
-5
@@ -36,8 +36,7 @@ import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketDecoder;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketEncoder;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketRegistry;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.login.LoginSuccessPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPreparer;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.play.DisconnectPacket;
|
||||
import xyz.jonesdev.sonar.common.statistics.GlobalSonarStatistics;
|
||||
|
||||
@@ -47,6 +46,8 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.FallbackPipelines.*;
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_20_2;
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketRegistry.CONFIG;
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketRegistry.GAME;
|
||||
|
||||
@Getter
|
||||
@ToString(of = {"protocolVersion", "inetAddress"})
|
||||
@@ -116,11 +117,10 @@ public final class FallbackUserWrapper implements FallbackUser {
|
||||
}
|
||||
|
||||
// Send LoginSuccess packet to make the client think they are joining the server
|
||||
write(new LoginSuccessPacket(username, uuid));
|
||||
write(FallbackPreparer.LOGIN_SUCCESS);
|
||||
|
||||
// The LoginSuccess packet has been sent, now we can change the registry state
|
||||
newEncoder.updateRegistry(protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_20_2) >= 0
|
||||
? FallbackPacketRegistry.CONFIG : FallbackPacketRegistry.GAME);
|
||||
newEncoder.updateRegistry(protocolVersion.compareTo(MINECRAFT_1_20_2) >= 0 ? CONFIG : GAME);
|
||||
|
||||
try {
|
||||
// Prepare custom packet decoder and verification handler
|
||||
|
||||
+30
-36
@@ -21,7 +21,6 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.CorruptedFrameException;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import lombok.val;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
@@ -32,14 +31,14 @@ import xyz.jonesdev.sonar.api.fallback.FallbackUserState;
|
||||
import xyz.jonesdev.sonar.api.model.VerifiedPlayer;
|
||||
import xyz.jonesdev.sonar.api.timer.SystemTimer;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.*;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.map.ItemType;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.map.MapCaptchaInfo;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.captcha.ItemType;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.captcha.MapCaptchaInfo;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.entity.EntityType;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.config.FinishConfigurationPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.login.LoginAcknowledgedPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.play.*;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.vehicle.EntityType;
|
||||
import xyz.jonesdev.sonar.common.statistics.GlobalSonarStatistics;
|
||||
import xyz.jonesdev.sonar.common.utility.protocol.ProtocolUtil;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
@@ -72,10 +71,8 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
private boolean receivedSteerBoat, receivedPlayerInput;
|
||||
|
||||
// CAPTCHA
|
||||
private final SystemTimer keepAlive = new SystemTimer();
|
||||
private final SystemTimer actionBar = new SystemTimer();
|
||||
private @Nullable MapCaptchaInfo captcha;
|
||||
private int captchaTriesLeft;
|
||||
private int captchaTriesLeft, lastCountdownIndex;
|
||||
|
||||
// Cached options to make sure the original values don't change throughout the verification
|
||||
private final boolean performVehicle, performCaptcha, performGravity, performCollisions, transfer;
|
||||
@@ -259,38 +256,35 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
|
||||
// Handle incoming chat messages
|
||||
if (packet instanceof UniversalChatPacket) {
|
||||
checkFrame(captcha != null, "Captcha not sent yet");
|
||||
final UniversalChatPacket chat = (UniversalChatPacket) packet;
|
||||
if (!chat.getMessage().equals(captcha.getAnswer())) {
|
||||
// Captcha is incorrect
|
||||
checkFrame(captchaTriesLeft-- > 0, "failed captcha too often");
|
||||
user.write(incorrectCaptcha);
|
||||
final String answer = Objects.requireNonNull(captcha).getAnswer();
|
||||
|
||||
if (chat.getMessage().equals(answer)) {
|
||||
// Captcha is correct
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
// Captcha is correct
|
||||
finish();
|
||||
|
||||
// Captcha is incorrect
|
||||
checkFrame(captchaTriesLeft-- > 0, "failed captcha too often");
|
||||
user.write(incorrectCaptcha);
|
||||
return;
|
||||
}
|
||||
|
||||
// Every second
|
||||
if (actionBar.elapsed(user.getProtocolVersion().compareTo(MINECRAFT_1_8) < 0 ? 10_000L : 1_000L)) {
|
||||
final String actionBarMessage = Sonar.get().getConfig().getVerification().getMap().getEnterCodeActionBar();
|
||||
// Only send action bar if the message is actually supposed to be sent
|
||||
if (!actionBarMessage.isEmpty()) {
|
||||
final String timeLeft = String.format("%.0f", (maxDuration - login.delay()) / 1000D);
|
||||
user.write(new UniversalChatPacket(MiniMessage.miniMessage().deserialize(
|
||||
actionBarMessage.replace("%time-left%", timeLeft)), UniversalChatPacket.GAME_INFO_TYPE));
|
||||
// Make sure to reset the timer
|
||||
actionBar.reset();
|
||||
// Only expect idle (position) packets
|
||||
if (packet instanceof PlayerPositionPacket
|
||||
|| packet instanceof PlayerPositionLookPacket) {
|
||||
// A position packet is sent every second
|
||||
final long difference = maxDuration - login.delay();
|
||||
final int index = (int) (difference / 1000D);
|
||||
// Make sure we can actually safely get and send the packet
|
||||
if (lastCountdownIndex != index && index >= 0 && xpCountdown.length > index) {
|
||||
// Send the countdown using the experience bar
|
||||
user.write(xpCountdown[index]);
|
||||
// Send a KeepAlive packet to prevent timeout
|
||||
user.write(CAPTCHA_KEEP_ALIVE);
|
||||
}
|
||||
}
|
||||
|
||||
// Every 10 seconds
|
||||
if (keepAlive.elapsed(10_000L)) {
|
||||
// Send a KeepAlive packet to prevent timeout
|
||||
user.write(CAPTCHA_KEEP_ALIVE);
|
||||
// Make sure to reset the timer
|
||||
keepAlive.reset();
|
||||
lastCountdownIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,7 +450,7 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
// The transaction should always be accepted
|
||||
checkFrame(transaction.isAccepted(), "transaction not accepted?!");
|
||||
// Check if the transaction ID is valid
|
||||
checkFrame(transaction.getId() == expectedTransactionId, "invalid transaction id");
|
||||
checkFrame(transaction.getTransactionId() == expectedTransactionId, "invalid transaction id");
|
||||
|
||||
// First, send an Abilities packet to the client to make
|
||||
// sure the player falls even in spectator mode.
|
||||
@@ -506,8 +500,8 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet instanceof PlayerTickPacket) {
|
||||
final PlayerTickPacket player = (PlayerTickPacket) packet;
|
||||
if (packet instanceof PlayerPositionIdlePacket) {
|
||||
final PlayerPositionIdlePacket player = (PlayerPositionIdlePacket) packet;
|
||||
handlePositionUpdate(posX, posY, posZ, player.isOnGround());
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -20,7 +20,10 @@ package xyz.jonesdev.sonar.common.fallback.protocol;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface FallbackPacket {
|
||||
UUID EMPTY_UUID = new UUID(0L, 0L);
|
||||
|
||||
/**
|
||||
* Encodes the packet sent by the server
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketRegistry.Direction.SERVERBOUND;
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketRegistry.GAME;
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketRegistry.LOGIN;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.readVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.readVarInt;
|
||||
|
||||
public final class FallbackPacketDecoder extends ChannelInboundHandlerAdapter {
|
||||
private final FallbackUser user;
|
||||
|
||||
+6
-4
@@ -25,7 +25,7 @@ import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketRegistry.Direction.CLIENTBOUND;
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketRegistry.LOGIN;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.writeVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
|
||||
|
||||
public final class FallbackPacketEncoder extends MessageToByteEncoder<FallbackPacket> {
|
||||
private final ProtocolVersion protocolVersion;
|
||||
@@ -42,10 +42,12 @@ public final class FallbackPacketEncoder extends MessageToByteEncoder<FallbackPa
|
||||
|
||||
@Override
|
||||
protected void encode(final ChannelHandlerContext ctx,
|
||||
final FallbackPacket msg,
|
||||
final @NotNull FallbackPacket packet,
|
||||
final ByteBuf out) throws Exception {
|
||||
final int packetId = registry.getPacketId(msg);
|
||||
final FallbackPacket originalPacket = packet instanceof FallbackPacketSnapshot
|
||||
? ((FallbackPacketSnapshot) packet).getOriginalPacket() : packet;
|
||||
final int packetId = registry.getPacketId(originalPacket);
|
||||
writeVarInt(out, packetId);
|
||||
msg.encode(out, protocolVersion);
|
||||
packet.encode(out, protocolVersion);
|
||||
}
|
||||
}
|
||||
+17
-2
@@ -33,7 +33,7 @@ import java.util.function.Supplier;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
|
||||
// Most of this is taken from
|
||||
// Mostly taken from
|
||||
// https://github.com/PaperMC/Velocity/blob/dev/3.0.0/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java
|
||||
public enum FallbackPacketRegistry {
|
||||
LOGIN {
|
||||
@@ -281,6 +281,21 @@ public enum FallbackPacketRegistry {
|
||||
map(0x12, MINECRAFT_1_19_3, true),
|
||||
map(0x14, MINECRAFT_1_19_4, true),
|
||||
map(0x15, MINECRAFT_1_20_2, true));
|
||||
clientbound.register(SetExperiencePacket.class, SetExperiencePacket::new,
|
||||
map(0x1F, MINECRAFT_1_7_2, true),
|
||||
map(0x3D, MINECRAFT_1_9, true),
|
||||
map(0x3F, MINECRAFT_1_12, true),
|
||||
map(0x40, MINECRAFT_1_12_1, true),
|
||||
map(0x43, MINECRAFT_1_13, true),
|
||||
map(0x47, MINECRAFT_1_14, true),
|
||||
map(0x48, MINECRAFT_1_15, true),
|
||||
map(0x51, MINECRAFT_1_17, true),
|
||||
map(0x54, MINECRAFT_1_19_1, true),
|
||||
map(0x52, MINECRAFT_1_19_3, true),
|
||||
map(0x56, MINECRAFT_1_19_4, true),
|
||||
map(0x58, MINECRAFT_1_20_2, true),
|
||||
map(0x5A, MINECRAFT_1_20_3, true),
|
||||
map(0x5C, MINECRAFT_1_20_5, true));
|
||||
clientbound.register(UniversalChatPacket.class, UniversalChatPacket::new,
|
||||
map(0x02, MINECRAFT_1_7_2, false),
|
||||
map(0x0F, MINECRAFT_1_9, false),
|
||||
@@ -416,7 +431,7 @@ public enum FallbackPacketRegistry {
|
||||
map(0x16, MINECRAFT_1_20_2, false),
|
||||
map(0x17, MINECRAFT_1_20_3, false),
|
||||
map(0x1A, MINECRAFT_1_20_5, false));
|
||||
serverbound.register(PlayerTickPacket.class, PlayerTickPacket::new,
|
||||
serverbound.register(PlayerPositionIdlePacket.class, PlayerPositionIdlePacket::new,
|
||||
map(0x03, MINECRAFT_1_7_2, false),
|
||||
map(0x0F, MINECRAFT_1_9, false),
|
||||
map(0x0D, MINECRAFT_1_12, false),
|
||||
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2024 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.common.fallback.protocol;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.ID_TO_PROTOCOL_CONSTANT;
|
||||
|
||||
// Mostly taken from
|
||||
// https://github.com/Nan1t/NanoLimbo/blob/main/src/main/java/ua/nanit/limbo/protocol/PacketSnapshot.java
|
||||
@Getter
|
||||
public final class FallbackPacketSnapshot implements FallbackPacket {
|
||||
private final Map<Integer, Integer> mappings = new HashMap<>(ID_TO_PROTOCOL_CONSTANT.size());
|
||||
private final Map<Integer, byte[]> cachedBytes = new HashMap<>(ID_TO_PROTOCOL_CONSTANT.size());
|
||||
private final FallbackPacket originalPacket;
|
||||
|
||||
public FallbackPacketSnapshot(final @NotNull FallbackPacket originalPacket) {
|
||||
final Map<Integer, Integer> hashes = new HashMap<>(ID_TO_PROTOCOL_CONSTANT.size());
|
||||
|
||||
for (final ProtocolVersion protocolVersion : ID_TO_PROTOCOL_CONSTANT.values()) {
|
||||
// Allocate a buffer for each protocol version
|
||||
final ByteBuf byteBuf = Unpooled.buffer();
|
||||
try {
|
||||
originalPacket.encode(byteBuf, protocolVersion);
|
||||
} catch (Exception exception) {
|
||||
Sonar.get().getLogger().error("Could not encode packet {} for version {}: {}",
|
||||
originalPacket.toString(), protocolVersion, exception);
|
||||
break;
|
||||
}
|
||||
|
||||
// Make sure we don't unnecessarily fill the RAM
|
||||
final int protocol = protocolVersion.getProtocol();
|
||||
final int hash = byteBuf.hashCode();
|
||||
final int hashed = hashes.getOrDefault(hash, -1);
|
||||
if (hashed != -1) {
|
||||
mappings.put(protocol, hashed);
|
||||
} else {
|
||||
hashes.put(hash, protocol);
|
||||
mappings.put(protocol, protocol);
|
||||
// Cache the raw bytes of the encoded packet
|
||||
final byte[] bytes = new byte[byteBuf.readableBytes()];
|
||||
byteBuf.readBytes(bytes);
|
||||
cachedBytes.put(protocol, bytes);
|
||||
}
|
||||
// Make sure to release the buffer to avoid memory leaks
|
||||
byteBuf.release();
|
||||
}
|
||||
this.originalPacket = originalPacket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(final @NotNull ByteBuf byteBuf,
|
||||
final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
final int hash = mappings.get(protocolVersion.getProtocol());
|
||||
final byte[] bytes = cachedBytes.get(hash);
|
||||
|
||||
if (bytes != null) {
|
||||
byteBuf.writeBytes(bytes);
|
||||
} else {
|
||||
Sonar.get().getLogger().error("Could not find cached packet {} for version {}",
|
||||
toString(), protocolVersion);
|
||||
throw new IllegalStateException("Unable to find cached packet. Contact the developer!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) throws Exception {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
+62
-43
@@ -20,36 +20,38 @@ package xyz.jonesdev.sonar.common.fallback.protocol;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.config.SonarConfiguration;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.block.BlockPosition;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.block.BlockType;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.block.ChangedBlock;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.block.BlockUpdate;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.captcha.CaptchaPreparer;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.dimension.DimensionRegistry;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.map.CaptchaPreparer;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.config.FinishConfigurationPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.config.RegistryDataPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.login.LoginSuccessPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.play.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
@UtilityClass
|
||||
public class FallbackPreparer {
|
||||
|
||||
// LoginSuccess
|
||||
public final FallbackPacket LOGIN_SUCCESS = new FallbackPacketSnapshot(new LoginSuccessPacket(new UUID(1L, 1L), "Sonar"));
|
||||
// Abilities
|
||||
public final FallbackPacket DEFAULT_ABILITIES = new ClientAbilitiesPacket(0x00, 0f, 0f);
|
||||
public final FallbackPacket CAPTCHA_ABILITIES = new ClientAbilitiesPacket(0x02, 0f, 0f);
|
||||
public final FallbackPacket CAPTCHA_ABILITIES_BEDROCK = new ClientAbilitiesPacket(0x06, 0f, 0f);
|
||||
public final FallbackPacket DEFAULT_ABILITIES = new FallbackPacketSnapshot(new ClientAbilitiesPacket(0x00, 0f, 0f));
|
||||
public final FallbackPacket CAPTCHA_ABILITIES = new FallbackPacketSnapshot(new ClientAbilitiesPacket(0x02, 0f, 0f));
|
||||
public final FallbackPacket CAPTCHA_ABILITIES_BEDROCK = new FallbackPacketSnapshot(new ClientAbilitiesPacket(0x06, 0f, 0f));
|
||||
// Chunks
|
||||
public final FallbackPacket EMPTY_CHUNK_DATA = new ChunkDataPacket(0, 0);
|
||||
public final FallbackPacket EMPTY_CHUNK_DATA = new FallbackPacketSnapshot(new ChunkDataPacket(0, 0));
|
||||
// Finish Configuration
|
||||
public final FallbackPacket FINISH_CONFIGURATION = new FinishConfigurationPacket();
|
||||
public final FallbackPacket FINISH_CONFIGURATION = new FinishConfigurationPacket(); // No snapshot needed because it's empty either way
|
||||
// Synchronize Registry
|
||||
public final FallbackPacket REGISTRY_SYNC_LEGACY = new RegistryDataPacket(DimensionRegistry.CODEC_1_20, null, null);
|
||||
public final List<FallbackPacket> REGISTRY_SYNC_1_20_5 = RegistryDataPacket.of(DimensionRegistry.CODEC_1_20);
|
||||
public final FallbackPacket REGISTRY_SYNC_LEGACY = new FallbackPacketSnapshot(new RegistryDataPacket(DimensionRegistry.CODEC_1_20, null, null));
|
||||
public final FallbackPacket[] REGISTRY_SYNC_1_20_5 = RegistryDataPacket.of(DimensionRegistry.CODEC_1_20);
|
||||
// Keep Alive
|
||||
public final FallbackPacket CAPTCHA_KEEP_ALIVE = new KeepAlivePacket(0L);
|
||||
public final FallbackPacket CAPTCHA_KEEP_ALIVE = new FallbackPacketSnapshot(new KeepAlivePacket(-1337L));
|
||||
// Game Event (1.20.3+)
|
||||
public final FallbackPacket START_WRITING_CHUNKS = new GameEventPacket(13, 0);
|
||||
public final FallbackPacket START_WRITING_CHUNKS = new FallbackPacketSnapshot(new GameEventPacket(13, 0));
|
||||
// Chat
|
||||
public FallbackPacket enterCodeMessage;
|
||||
public FallbackPacket incorrectCaptcha;
|
||||
@@ -84,13 +86,13 @@ public class FallbackPreparer {
|
||||
public final int DEFAULT_Y_COLLIDE_POSITION = 255; // 255 is the maximum Y position allowed
|
||||
|
||||
// Captcha position
|
||||
public final FallbackPacket CAPTCHA_POSITION = new PlayerPositionLookPacket(
|
||||
SPAWN_X_POSITION, 1337, SPAWN_Z_POSITION, 0f, 90f, 0, false);
|
||||
public final FallbackPacket CAPTCHA_SPAWN_POSITION = new DefaultSpawnPositionPacket(
|
||||
SPAWN_X_POSITION, 1337, SPAWN_Z_POSITION);
|
||||
public final FallbackPacket CAPTCHA_POSITION = new FallbackPacketSnapshot(new PlayerPositionLookPacket(
|
||||
SPAWN_X_POSITION, 1337, SPAWN_Z_POSITION, 0f, 90f, 0, false));
|
||||
public final FallbackPacket CAPTCHA_SPAWN_POSITION = new FallbackPacketSnapshot(new DefaultSpawnPositionPacket(
|
||||
SPAWN_X_POSITION, 1337, SPAWN_Z_POSITION));
|
||||
|
||||
// Blocks
|
||||
private final ChangedBlock[] CHANGED_BLOCKS = new ChangedBlock[BLOCKS_PER_ROW * BLOCKS_PER_ROW];
|
||||
private final BlockUpdate[] CHANGED_BLOCKS = new BlockUpdate[BLOCKS_PER_ROW * BLOCKS_PER_ROW];
|
||||
public BlockType blockType = BlockType.BARRIER;
|
||||
|
||||
public int maxMovementTick, dynamicSpawnYPosition;
|
||||
@@ -100,13 +102,18 @@ public class FallbackPreparer {
|
||||
// CAPTCHA
|
||||
public final CaptchaPreparer MAP_INFO_PREPARER = new CaptchaPreparer();
|
||||
|
||||
// XP packets
|
||||
public FallbackPacket[] xpCountdown;
|
||||
|
||||
public void prepare() {
|
||||
// Prepare JoinGame packet
|
||||
joinGame = new JoinGamePacket(PLAYER_ENTITY_ID,
|
||||
Sonar.get().getConfig().getVerification().getGravity().getGamemode().getId(),
|
||||
0, false, 0,
|
||||
true, false, false,
|
||||
new String[]{"minecraft:overworld"}, "minecraft:overworld");
|
||||
|
||||
// Prepare cached motions for the gravity check
|
||||
maxFallDistance = 0;
|
||||
maxMovementTick = Sonar.get().getConfig().getVerification().getGravity().getMaxMovementTicks();
|
||||
preparedCachedYMotions = new double[maxMovementTick + 8];
|
||||
@@ -119,49 +126,61 @@ public class FallbackPreparer {
|
||||
|
||||
// Set the dynamic block and collide Y position based on the maximum fall distance
|
||||
dynamicSpawnYPosition = DEFAULT_Y_COLLIDE_POSITION + (int) Math.ceil(maxFallDistance);
|
||||
dynamicSpawnPosition = new DefaultSpawnPositionPacket(SPAWN_X_POSITION, dynamicSpawnYPosition, SPAWN_Z_POSITION);
|
||||
dynamicSpawnPosition = new FallbackPacketSnapshot(new DefaultSpawnPositionPacket(
|
||||
SPAWN_X_POSITION, dynamicSpawnYPosition, SPAWN_Z_POSITION));
|
||||
|
||||
// Prepare collision platform positions
|
||||
int index = 0;
|
||||
for (int x = 0; x < BLOCKS_PER_ROW; x++) {
|
||||
for (int z = 0; z < BLOCKS_PER_ROW; z++) {
|
||||
final BlockPosition position = new BlockPosition(
|
||||
final BlockUpdate.BlockPosition position = new BlockUpdate.BlockPosition(
|
||||
x + (BLOCKS_PER_ROW / 2),
|
||||
DEFAULT_Y_COLLIDE_POSITION,
|
||||
z + (BLOCKS_PER_ROW / 2),
|
||||
0, 0);
|
||||
CHANGED_BLOCKS[index++] = new ChangedBlock(position, blockType);
|
||||
CHANGED_BLOCKS[index++] = new BlockUpdate(position, blockType);
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare UpdateSectionBlocks packet
|
||||
updateSectionBlocks = new UpdateSectionBlocksPacket(0, 0, CHANGED_BLOCKS);
|
||||
updateSectionBlocks = new FallbackPacketSnapshot(new UpdateSectionBlocksPacket(0, 0, CHANGED_BLOCKS));
|
||||
|
||||
// Prepare disconnect packets during login
|
||||
blacklisted = DisconnectPacket.create(Sonar.get().getConfig().getVerification().getBlacklisted(), true);
|
||||
alreadyVerifying = DisconnectPacket.create(Sonar.get().getConfig().getVerification().getAlreadyVerifying(), true);
|
||||
alreadyQueued = DisconnectPacket.create(Sonar.get().getConfig().getVerification().getAlreadyQueued(), true);
|
||||
protocolBlacklisted = DisconnectPacket.create(Sonar.get().getConfig().getVerification().getProtocolBlacklisted(), true);
|
||||
reconnectedTooFast = DisconnectPacket.create(Sonar.get().getConfig().getVerification().getTooFastReconnect(), true);
|
||||
invalidUsername = DisconnectPacket.create(Sonar.get().getConfig().getVerification().getInvalidUsername(), true);
|
||||
tooManyOnlinePerIP = DisconnectPacket.create(Sonar.get().getConfig().getTooManyOnlinePerIp(), true);
|
||||
blacklisted = new FallbackPacketSnapshot(DisconnectPacket.create(Sonar.get().getConfig().getVerification().getBlacklisted(), true));
|
||||
alreadyVerifying = new FallbackPacketSnapshot(DisconnectPacket.create(Sonar.get().getConfig().getVerification().getAlreadyVerifying(), true));
|
||||
alreadyQueued = new FallbackPacketSnapshot(DisconnectPacket.create(Sonar.get().getConfig().getVerification().getAlreadyQueued(), true));
|
||||
protocolBlacklisted = new FallbackPacketSnapshot(DisconnectPacket.create(Sonar.get().getConfig().getVerification().getProtocolBlacklisted(), true));
|
||||
reconnectedTooFast = new FallbackPacketSnapshot(DisconnectPacket.create(Sonar.get().getConfig().getVerification().getTooFastReconnect(), true));
|
||||
invalidUsername = new FallbackPacketSnapshot(DisconnectPacket.create(Sonar.get().getConfig().getVerification().getInvalidUsername(), true));
|
||||
tooManyOnlinePerIP = new FallbackPacketSnapshot(DisconnectPacket.create(Sonar.get().getConfig().getTooManyOnlinePerIp(), true));
|
||||
|
||||
// Precompute captcha answers
|
||||
if (Sonar.get().getConfig().getVerification().getMap().getTiming() != SonarConfiguration.Verification.Timing.NEVER
|
||||
|| Sonar.get().getConfig().getVerification().getGravity().isCaptchaOnFail()) {
|
||||
enterCodeMessage = new UniversalChatPacket(Sonar.get().getConfig().getVerification().getMap().getEnterCode(), UniversalChatPacket.SYSTEM_TYPE);
|
||||
incorrectCaptcha = new UniversalChatPacket(
|
||||
Sonar.get().getConfig().getVerification().getMap().getFailedCaptcha(), UniversalChatPacket.SYSTEM_TYPE);
|
||||
|
||||
MAP_INFO_PREPARER.prepare();
|
||||
}
|
||||
|
||||
transferToOrigin = new TransferPacket(
|
||||
// Prepare transfer packet
|
||||
transferToOrigin = new FallbackPacketSnapshot(new TransferPacket(
|
||||
Sonar.get().getConfig().getVerification().getTransfer().getHost(),
|
||||
Sonar.get().getConfig().getVerification().getTransfer().getPort());
|
||||
Sonar.get().getConfig().getVerification().getTransfer().getPort()));
|
||||
|
||||
// Prepare packets for the vehicle check
|
||||
removeEntities = new RemoveEntitiesPacket(VEHICLE_ENTITY_ID);
|
||||
setPassengers = new SetPassengersPacket(VEHICLE_ENTITY_ID, PLAYER_ENTITY_ID);
|
||||
removeEntities = new FallbackPacketSnapshot(new RemoveEntitiesPacket(VEHICLE_ENTITY_ID));
|
||||
setPassengers = new FallbackPacketSnapshot(new SetPassengersPacket(VEHICLE_ENTITY_ID, PLAYER_ENTITY_ID));
|
||||
|
||||
if (Sonar.get().getConfig().getVerification().getMap().getTiming() != SonarConfiguration.Verification.Timing.NEVER
|
||||
|| Sonar.get().getConfig().getVerification().getGravity().isCaptchaOnFail()) {
|
||||
// Prepare CAPTCHA messages
|
||||
enterCodeMessage = new FallbackPacketSnapshot(new UniversalChatPacket(
|
||||
Sonar.get().getConfig().getVerification().getMap().getEnterCode(), UniversalChatPacket.SYSTEM_TYPE));
|
||||
incorrectCaptcha = new FallbackPacketSnapshot(new UniversalChatPacket(
|
||||
Sonar.get().getConfig().getVerification().getMap().getFailedCaptcha(), UniversalChatPacket.SYSTEM_TYPE));
|
||||
|
||||
// Prepare countdown
|
||||
xpCountdown = new FallbackPacket[Sonar.get().getConfig().getVerification().getMap().getMaxDuration() / 1000];
|
||||
|
||||
for (int i = 0; i < xpCountdown.length; i++) {
|
||||
final float bar = (float) i / xpCountdown.length;
|
||||
xpCountdown[i] = new FallbackPacketSnapshot(new SetExperiencePacket(bar, i, i));
|
||||
}
|
||||
|
||||
// Prepare CAPTCHA answers
|
||||
MAP_INFO_PREPARER.prepare();
|
||||
}
|
||||
}
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2024 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.common.fallback.protocol.block;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@RequiredArgsConstructor
|
||||
public final class BlockPosition {
|
||||
private final int x, y, z;
|
||||
private final int chunkX, chunkZ;
|
||||
}
|
||||
+12
-2
@@ -18,15 +18,17 @@
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.block;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Getter
|
||||
public final class ChangedBlock {
|
||||
public final class BlockUpdate {
|
||||
private final @NotNull BlockPosition position;
|
||||
private final BlockType type;
|
||||
private final int legacyBlockState, blockState;
|
||||
|
||||
public ChangedBlock(final @NotNull BlockPosition position, final BlockType type) {
|
||||
public BlockUpdate(final @NotNull BlockPosition position, final BlockType type) {
|
||||
this.position = position;
|
||||
this.type = type;
|
||||
final int x = position.getX() - (position.getChunkX() << 4);
|
||||
@@ -35,4 +37,12 @@ public final class ChangedBlock {
|
||||
this.legacyBlockState = x << 12 | z << 8 | y;
|
||||
this.blockState = x << 8 | z << 4 | y - ((y >> 4) << 4);
|
||||
}
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@RequiredArgsConstructor
|
||||
public static final class BlockPosition {
|
||||
private final int x, y, z;
|
||||
private final int chunkX, chunkZ;
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2024 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.common.fallback.protocol.captcha;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import xyz.jonesdev.capja.SimpleCaptchaGenerator;
|
||||
import xyz.jonesdev.capja.filter.SimpleRippleFilter;
|
||||
import xyz.jonesdev.capja.filter.SimpleScratchFilter;
|
||||
import xyz.jonesdev.capja.libs.jhlabs.image.AbstractBufferedImageOp;
|
||||
import xyz.jonesdev.capja.libs.jhlabs.image.BumpFilter;
|
||||
import xyz.jonesdev.capja.libs.jhlabs.image.SmearFilter;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.config.SonarConfiguration;
|
||||
import xyz.jonesdev.sonar.api.timer.SystemTimer;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public final class CaptchaPreparer {
|
||||
private static final ExecutorService PREPARATION_SERVICE = Executors.newSingleThreadExecutor();
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
private MapCaptchaInfo[] cached;
|
||||
private int preparedAmount;
|
||||
|
||||
public void prepare() {
|
||||
// Make sure we're not preparing when Sonar is already preparing answers
|
||||
if (cached != null && preparedAmount != cached.length) return;
|
||||
preparedAmount = 0;
|
||||
|
||||
final SystemTimer timer = new SystemTimer();
|
||||
Sonar.get().getLogger().info("Asynchronously preparing CAPTCHA answers...");
|
||||
Sonar.get().getLogger().info("Players will be able to join even if the preparation isn't finished");
|
||||
|
||||
// Prepare cache
|
||||
final SonarConfiguration.Verification.Map config = Sonar.get().getConfig().getVerification().getMap();
|
||||
cached = new MapCaptchaInfo[config.getPrecomputeAmount()];
|
||||
|
||||
// Prepare everything asynchronously
|
||||
PREPARATION_SERVICE.execute(() -> {
|
||||
// Create the images using capja
|
||||
final @Nullable File backgroundImage = Sonar.get().getConfig().getVerification().getMap().getBackgroundImage();
|
||||
final SimpleCaptchaGenerator generator = new SimpleCaptchaGenerator(128, 128, backgroundImage);
|
||||
final char[] dictionary = config.getDictionary().toCharArray();
|
||||
|
||||
final SimpleScratchFilter scratchFilter = new SimpleScratchFilter(5);
|
||||
final BumpFilter bumpFilter = new BumpFilter();
|
||||
|
||||
for (preparedAmount = 0; preparedAmount < config.getPrecomputeAmount(); preparedAmount++) {
|
||||
// Prepare the filters per CAPTCHA for some more randomness
|
||||
final List<AbstractBufferedImageOp> filters = new ArrayList<>();
|
||||
if (Sonar.get().getConfig().getVerification().getMap().isScratches()) {
|
||||
filters.add(scratchFilter);
|
||||
}
|
||||
if (Sonar.get().getConfig().getVerification().getMap().isRipple()) {
|
||||
final SimpleRippleFilter rippleFilter = new SimpleRippleFilter();
|
||||
rippleFilter.setXAmplitude(0);
|
||||
float yAmplitude = 10 - ThreadLocalRandom.current().nextInt(20);
|
||||
if (Math.abs(yAmplitude) < 3) {
|
||||
yAmplitude = yAmplitude >= 0 ? 3 : -3;
|
||||
}
|
||||
rippleFilter.setYAmplitude(yAmplitude);
|
||||
filters.add(rippleFilter);
|
||||
}
|
||||
if (Sonar.get().getConfig().getVerification().getMap().getDistortion().isEnabled()) {
|
||||
final SmearFilter smearFilter = new SmearFilter();
|
||||
smearFilter.setShape(Sonar.get().getConfig().getVerification().getMap().getDistortion().getShape());
|
||||
smearFilter.setMix(Sonar.get().getConfig().getVerification().getMap().getDistortion().getMix());
|
||||
smearFilter.setDensity(Sonar.get().getConfig().getVerification().getMap().getDistortion().getDensity());
|
||||
smearFilter.setDistance(Sonar.get().getConfig().getVerification().getMap().getDistortion().getDistance());
|
||||
filters.add(smearFilter);
|
||||
}
|
||||
if (Sonar.get().getConfig().getVerification().getMap().isBump()) {
|
||||
filters.add(bumpFilter);
|
||||
}
|
||||
if (!Sonar.get().getConfig().getVerification().getMap().isAutoColor()) {
|
||||
// Generate a random gradient color if automatic coloring is disabled
|
||||
final Color color0 = Color.getHSBColor((float) Math.random(), 1, 1);
|
||||
final Color color1 = Color.getHSBColor((float) Math.random(), 1, 0.5f);
|
||||
final GradientPaint gradient = new GradientPaint(0, 0, color0,
|
||||
generator.getWidth(), generator.getHeight(), color1);
|
||||
generator.setGradient(gradient);
|
||||
}
|
||||
// Generate CAPTCHA answer
|
||||
final char[] answer = new char[5];
|
||||
for (int j = 0; j < answer.length; j++) {
|
||||
answer[j] = dictionary[ThreadLocalRandom.current().nextInt(dictionary.length)];
|
||||
}
|
||||
final BufferedImage image = generator.createImage(answer, filters);
|
||||
// Convert and cache converted Minecraft map bytes
|
||||
final int[] buffer = MapColorPalette.getBufferFromImage(image);
|
||||
cached[preparedAmount] = new MapCaptchaInfo(new String(answer), buffer);
|
||||
}
|
||||
|
||||
Sonar.get().getLogger().info("Finished preparing {} CAPTCHA answers ({}s)!", preparedAmount, timer);
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isCaptchaAvailable() {
|
||||
return cached != null && cached[0] != null;
|
||||
}
|
||||
|
||||
public MapCaptchaInfo getRandomCaptcha() {
|
||||
// Give the player a random CAPTCHA out of the ones that we've already prepared
|
||||
return cached[RANDOM.nextInt(preparedAmount)];
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.map;
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.captcha;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
+16
-17
@@ -15,11 +15,13 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.map;
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.captcha;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketSnapshot;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.play.MapDataPacket;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_8;
|
||||
@@ -27,39 +29,36 @@ import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT
|
||||
@Getter
|
||||
public final class MapCaptchaInfo {
|
||||
private final String answer;
|
||||
private final byte[] buffer;
|
||||
|
||||
private final MapDataPacket[] legacy;
|
||||
private final MapDataPacket modern;
|
||||
private final FallbackPacket[] legacy;
|
||||
private final FallbackPacket modern;
|
||||
|
||||
public MapCaptchaInfo(final @NotNull String answer, final byte @NotNull [] buffer) {
|
||||
public MapCaptchaInfo(final @NotNull String answer, final int @NotNull [] buffer) {
|
||||
this.answer = answer;
|
||||
this.buffer = buffer;
|
||||
|
||||
// Prepare 1.7 map data using a grid
|
||||
final byte[][] grid = new byte[128][128];
|
||||
final int[][] grid = new int[128][128];
|
||||
for (int i = 0; i < buffer.length; i++) {
|
||||
final byte buf = buffer[i];
|
||||
final int buf = buffer[i];
|
||||
grid[i & Byte.MAX_VALUE][i >> 7] = buf;
|
||||
}
|
||||
this.legacy = new MapDataPacket[grid.length];
|
||||
this.legacy = new FallbackPacket[grid.length];
|
||||
for (int i = 0; i < grid.length; i++) {
|
||||
this.legacy[i] = new MapDataPacket(grid[i], i, 0);
|
||||
this.legacy[i] = new FallbackPacketSnapshot(new MapDataPacket(grid[i], i, 0));
|
||||
}
|
||||
|
||||
// Prepare 1.8+ map data
|
||||
this.modern = new MapDataPacket(buffer, 0, 0);
|
||||
this.modern = new FallbackPacketSnapshot(new MapDataPacket(buffer, 0, 0));
|
||||
}
|
||||
|
||||
public void delayedWrite(final @NotNull FallbackUser user) {
|
||||
if (user.getProtocolVersion().compareTo(MINECRAFT_1_8) >= 0) {
|
||||
user.delayedWrite(modern);
|
||||
return;
|
||||
}
|
||||
|
||||
// 1.7.2-1.7.10
|
||||
for (final MapDataPacket legacyPacket : legacy) {
|
||||
user.delayedWrite(legacyPacket);
|
||||
} else {
|
||||
// 1.7.2-1.7.10
|
||||
for (final FallbackPacket legacyPacket : legacy) {
|
||||
user.delayedWrite(legacyPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
-28
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.map;
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.captcha;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -26,10 +26,10 @@ import java.awt.image.BufferedImage;
|
||||
public class MapColorPalette {
|
||||
// https://minecraft.wiki/w/Map_item_format#Full_color_tables
|
||||
public static final int[] COLOR_MAP = {
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
-16777216,
|
||||
-16777216,
|
||||
-16777216,
|
||||
-16777216,
|
||||
-10912473,
|
||||
-9594576,
|
||||
-8408520,
|
||||
@@ -38,10 +38,10 @@ public class MapColorPalette {
|
||||
-2766452,
|
||||
-530013,
|
||||
-8225962,
|
||||
-7697782,
|
||||
-5658199,
|
||||
-3815995,
|
||||
-9934744,
|
||||
-7566196,
|
||||
-5526613,
|
||||
-3684409,
|
||||
-9868951,
|
||||
-4980736,
|
||||
-2359296,
|
||||
-65536,
|
||||
@@ -66,22 +66,22 @@ public class MapColorPalette {
|
||||
-7499618,
|
||||
-5986120,
|
||||
-11118495,
|
||||
-8304095,
|
||||
-6464728,
|
||||
-4756945,
|
||||
-10471400,
|
||||
-9810890,
|
||||
-8233406,
|
||||
-6853299,
|
||||
-11585240,
|
||||
-11579569,
|
||||
-10461088,
|
||||
-9408400,
|
||||
-12895429,
|
||||
-10136525,
|
||||
-8689858,
|
||||
-7375032,
|
||||
-11780314,
|
||||
-13816396,
|
||||
-13158436,
|
||||
-12566273,
|
||||
-14605945,
|
||||
-10202062,
|
||||
-8690114,
|
||||
-7375032,
|
||||
-11845850,
|
||||
-4935252,
|
||||
-2303533,
|
||||
-779,
|
||||
@@ -153,19 +153,34 @@ public class MapColorPalette {
|
||||
-12543338,
|
||||
-11551561,
|
||||
-10691627,
|
||||
-13601936,
|
||||
-13346124,
|
||||
-12620068,
|
||||
-11894529,
|
||||
-14204025,
|
||||
-16738008,
|
||||
-16729294,
|
||||
-16721606,
|
||||
-16748002,
|
||||
-10798046,
|
||||
-9483734,
|
||||
-8301007,
|
||||
-12309223,
|
||||
-11599616,
|
||||
-10485504,
|
||||
-9436672,
|
||||
-12910336,
|
||||
};
|
||||
|
||||
public byte[] getBufferFromImage(final @NotNull BufferedImage bufferedImage) {
|
||||
final int width = bufferedImage.getWidth();
|
||||
final int height = bufferedImage.getHeight();
|
||||
public int[] getBufferFromImage(final @NotNull BufferedImage bufferedImage) {
|
||||
final int[] buffer = new int[128 * 128];
|
||||
|
||||
final int[] pixels = new int[128 * 128];
|
||||
final byte[] buffer = new byte[128 * 128];
|
||||
bufferedImage.getRGB(0, 0,
|
||||
bufferedImage.getWidth(), bufferedImage.getHeight(),
|
||||
buffer, 0, bufferedImage.getWidth());
|
||||
|
||||
bufferedImage.getRGB(0, 0, width, height, pixels, 0, width);
|
||||
|
||||
for (int i = 0; i < pixels.length; i++) {
|
||||
buffer[i] = mapColorToMinecraft(pixels[i]);
|
||||
for (int i = 0; i < buffer.length; i++) {
|
||||
buffer[i] = mapColorToMinecraft(buffer[i]);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
@@ -173,11 +188,12 @@ public class MapColorPalette {
|
||||
private byte mapColorToMinecraft(final int argb) {
|
||||
int minDistance = Integer.MAX_VALUE;
|
||||
byte closestIndex = 0;
|
||||
for (byte i = 0; i < COLOR_MAP.length; i++) {
|
||||
for (int i = 0; i < COLOR_MAP.length; i++) {
|
||||
final byte index = (byte) (i < 128 ? i : -129 + (i - 127));
|
||||
final int distance = calculateColorDistance(argb, COLOR_MAP[i]);
|
||||
if (distance < minDistance) {
|
||||
minDistance = distance;
|
||||
closestIndex = i;
|
||||
closestIndex = index;
|
||||
}
|
||||
}
|
||||
return closestIndex;
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.vehicle;
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.entity;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
-87
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2024 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.common.fallback.protocol.map;
|
||||
|
||||
import xyz.jonesdev.capja.CaptchaGenerator;
|
||||
import xyz.jonesdev.capja.CaptchaHolder;
|
||||
import xyz.jonesdev.capja.config.CaptchaConfiguration;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.config.SonarConfiguration;
|
||||
import xyz.jonesdev.sonar.api.timer.SystemTimer;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public final class CaptchaPreparer {
|
||||
private static final ExecutorService PREPARATION_SERVICE = Executors.newSingleThreadExecutor();
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
private MapCaptchaInfo[] cached;
|
||||
private int preparedAmount;
|
||||
|
||||
public void prepare() {
|
||||
// Make sure we're not preparing when Sonar is already preparing answers
|
||||
if (cached != null && preparedAmount != cached.length) return;
|
||||
preparedAmount = 0;
|
||||
|
||||
final SystemTimer timer = new SystemTimer();
|
||||
Sonar.get().getLogger().info("Asynchronously preparing CAPTCHA answers...");
|
||||
Sonar.get().getLogger().info("Players will be able to join even if the preparation isn't finished");
|
||||
|
||||
// Prepare cache
|
||||
final SonarConfiguration.Verification.Map config = Sonar.get().getConfig().getVerification().getMap();
|
||||
cached = new MapCaptchaInfo[config.getPrecomputeAmount()];
|
||||
|
||||
// Prepare everything asynchronously
|
||||
PREPARATION_SERVICE.execute(() -> {
|
||||
final List<String> fonts = config.getFonts();
|
||||
if (fonts.isEmpty()) {
|
||||
throw new IllegalArgumentException("No fonts specified!");
|
||||
}
|
||||
|
||||
// Create the configuration for the image generation
|
||||
final String[] fontNames = fonts.toArray(new String[0]);
|
||||
final CaptchaConfiguration configuration = new CaptchaConfiguration(
|
||||
128, 128, config.getDictionary().toCharArray(), 5,
|
||||
config.isFlare(), config.isScratches(), config.isRipple(), config.isSmear(), config.isPinch(),
|
||||
config.getSaturation(), config.getDistortion(), new int[]{Font.PLAIN, Font.BOLD}, fontNames);
|
||||
|
||||
for (int i = 0; i < config.getPrecomputeAmount(); i++) {
|
||||
final CaptchaGenerator captchaGenerator = new CaptchaGenerator(configuration);
|
||||
final CaptchaHolder captchaHolder = captchaGenerator.generate();
|
||||
final byte[] buffer = MapColorPalette.getBufferFromImage(captchaHolder.getImage());
|
||||
cached[i] = new MapCaptchaInfo(captchaHolder.getAnswer(), buffer);
|
||||
preparedAmount++;
|
||||
}
|
||||
|
||||
Sonar.get().getLogger().info("Finished preparing {} CAPTCHA answers ({}s)!", preparedAmount, timer);
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isCaptchaAvailable() {
|
||||
return cached != null && cached[0] != null;
|
||||
}
|
||||
|
||||
public MapCaptchaInfo getRandomCaptcha() {
|
||||
// Give the player a random CAPTCHA out of the ones that we've already prepared
|
||||
return cached[RANDOM.nextInt(preparedAmount)];
|
||||
}
|
||||
}
|
||||
+7
-8
@@ -25,16 +25,14 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketSnapshot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.ProtocolUtil.writeNamelessCompoundTag;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.ProtocolUtil.writeString;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.writeVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.*;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class RegistryDataPacket implements FallbackPacket {
|
||||
@@ -46,7 +44,7 @@ public final class RegistryDataPacket implements FallbackPacket {
|
||||
public void encode(final @NotNull ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) {
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_20_5) < 0) {
|
||||
writeNamelessCompoundTag(byteBuf, tag);
|
||||
} else {
|
||||
} else if (type != null) {
|
||||
writeString(byteBuf, type);
|
||||
writeVarInt(byteBuf, bundles.size());
|
||||
|
||||
@@ -69,15 +67,16 @@ public final class RegistryDataPacket implements FallbackPacket {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public static @NotNull List<FallbackPacket> of(final @NotNull CompoundBinaryTag rootTag) {
|
||||
final List<FallbackPacket> packets = new ArrayList<>();
|
||||
public static FallbackPacket @NotNull [] of(final @NotNull CompoundBinaryTag rootTag) {
|
||||
final FallbackPacket[] packets = new FallbackPacket[rootTag.size()];
|
||||
int index = 0;
|
||||
for (final String type : rootTag.keySet()) {
|
||||
final ArrayList<RegistryDataPacket.Bundle> bundles = new ArrayList<>();
|
||||
for (final BinaryTag binaryTag : rootTag.getCompound(type).getList("value")) {
|
||||
final CompoundBinaryTag tag = (CompoundBinaryTag) binaryTag;
|
||||
bundles.add(new Bundle(tag.getString("name"), tag.getCompound("element")));
|
||||
}
|
||||
packets.add(new RegistryDataPacket(rootTag, type, bundles));
|
||||
packets[index++] = new FallbackPacketSnapshot(new RegistryDataPacket(rootTag, type, bundles));
|
||||
}
|
||||
return packets;
|
||||
}
|
||||
|
||||
+1
@@ -25,6 +25,7 @@ public final class LoginAcknowledgedPacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-5
@@ -24,22 +24,21 @@ import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.netty.FastUUID;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.common.util.FastUuidSansHyphens;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.ProtocolUtil.*;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.writeVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.*;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class LoginSuccessPacket implements FallbackPacket {
|
||||
private String username;
|
||||
private UUID uuid;
|
||||
private String username;
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) {
|
||||
@@ -50,7 +49,7 @@ public final class LoginSuccessPacket implements FallbackPacket {
|
||||
} else if (protocolVersion.compareTo(MINECRAFT_1_7_6) >= 0) {
|
||||
writeString(byteBuf, uuid.toString());
|
||||
} else {
|
||||
writeString(byteBuf, FastUUID.toString(uuid));
|
||||
writeString(byteBuf, FastUuidSansHyphens.toString(uuid));
|
||||
}
|
||||
|
||||
writeString(byteBuf, username);
|
||||
|
||||
+1
-2
@@ -29,8 +29,7 @@ import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.ProtocolUtil.*;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.writeVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.*;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
|
||||
+3
-3
@@ -27,8 +27,8 @@ import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.ProtocolUtil.readString;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.readVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.readString;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.readVarInt;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@@ -39,7 +39,7 @@ public final class ClientSettingsPacket implements FallbackPacket {
|
||||
private byte viewDistance;
|
||||
private int chatVisibility;
|
||||
private boolean chatColors;
|
||||
private byte difficulty; // 1.7 Protocol
|
||||
private byte difficulty; // 1.7
|
||||
private short skinParts;
|
||||
private int mainHand;
|
||||
private boolean chatFilteringEnabled; // Added in 1.17
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.common.utility.component.ComponentHolder;
|
||||
import xyz.jonesdev.sonar.common.util.ComponentHolder;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
|
||||
+1
-2
@@ -27,8 +27,7 @@ import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.dimension.DimensionRegistry.*;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.ProtocolUtil.*;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.writeVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.*;
|
||||
|
||||
// Mostly taken from
|
||||
// https://github.com/Nan1t/NanoLimbo/blob/main/src/main/java/ua/nanit/limbo/protocol/packets/play/PacketJoinGame.java
|
||||
|
||||
+2
-2
@@ -28,8 +28,8 @@ import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_12_2;
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_8;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.readVarInt;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.writeVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.readVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
|
||||
+10
-7
@@ -21,19 +21,17 @@ import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.writeVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class MapDataPacket implements FallbackPacket {
|
||||
private byte[] buffer;
|
||||
private int[] buffer;
|
||||
private int x, y;
|
||||
|
||||
@Override
|
||||
@@ -45,8 +43,7 @@ public final class MapDataPacket implements FallbackPacket {
|
||||
byteBuf.writeByte(0); // scaling
|
||||
byteBuf.writeByte(x);
|
||||
byteBuf.writeByte(y);
|
||||
|
||||
byteBuf.writeBytes(buffer);
|
||||
writeArray(byteBuf, buffer);
|
||||
} else {
|
||||
byteBuf.writeByte(0); // scaling
|
||||
|
||||
@@ -71,7 +68,13 @@ public final class MapDataPacket implements FallbackPacket {
|
||||
byteBuf.writeByte(y);
|
||||
|
||||
writeVarInt(byteBuf, buffer.length);
|
||||
byteBuf.writeBytes(buffer);
|
||||
writeArray(byteBuf, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeArray(final ByteBuf byteBuf, final int @NotNull [] array) {
|
||||
for (final int i : array) {
|
||||
byteBuf.writeByte(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PlayerInputPacket implements FallbackPacket {
|
||||
public final class PlayerInputPacket implements FallbackPacket {
|
||||
private float sideways, forward;
|
||||
private boolean jump, unmount;
|
||||
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class PlayerTickPacket implements FallbackPacket {
|
||||
public final class PlayerPositionIdlePacket implements FallbackPacket {
|
||||
private boolean onGround;
|
||||
|
||||
@Override
|
||||
+1
-1
@@ -27,7 +27,7 @@ import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.writeVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_13;
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_8;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.ProtocolUtil.*;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.*;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.writeVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2024 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.common.fallback.protocol.packets.play;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class SetExperiencePacket implements FallbackPacket {
|
||||
private float experienceBar;
|
||||
private int level;
|
||||
private int totalExperience;
|
||||
|
||||
@Override
|
||||
public void encode(final @NotNull ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) {
|
||||
byteBuf.writeFloat(experienceBar);
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_8) < 0) {
|
||||
byteBuf.writeShort(level);
|
||||
byteBuf.writeShort(totalExperience);
|
||||
} else {
|
||||
writeVarInt(byteBuf, level);
|
||||
writeVarInt(byteBuf, totalExperience);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -26,15 +26,14 @@ import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.writeVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class SetPassengersPacket implements FallbackPacket {
|
||||
private int entityId;
|
||||
private int passengerId;
|
||||
private int entityId, passengerId;
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) {
|
||||
|
||||
+3
-6
@@ -29,18 +29,14 @@ import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.ProtocolUtil.writeCompoundTag;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.ProtocolUtil.writeNamelessCompoundTag;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.writeVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.*;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class SetSlotPacket implements FallbackPacket {
|
||||
private int slot;
|
||||
private int count;
|
||||
private int itemId;
|
||||
private int slot, count, itemId;
|
||||
private CompoundBinaryTag compoundBinaryTag;
|
||||
|
||||
public static final CompoundBinaryTag MAP_NBT = CompoundBinaryTag.builder()
|
||||
@@ -91,6 +87,7 @@ public final class SetSlotPacket implements FallbackPacket {
|
||||
} else if (protocolVersion.compareTo(MINECRAFT_1_20_5) < 0) {
|
||||
writeNamelessCompoundTag(byteBuf, compoundBinaryTag);
|
||||
} else { // 1.20.5
|
||||
// TODO: find a way to improve this
|
||||
// component
|
||||
writeVarInt(byteBuf, 1); // component count to add
|
||||
writeVarInt(byteBuf, 0); // component count to remove
|
||||
|
||||
+2
-3
@@ -27,9 +27,8 @@ import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.common.fallback.netty.FastUUID.EMPTY_UUID;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.ProtocolUtil.writeUUID;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.writeVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeUUID;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ import lombok.ToString;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.readVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.readVarInt;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
|
||||
+5
-5
@@ -33,17 +33,17 @@ import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class TransactionPacket implements FallbackPacket {
|
||||
private int windowId, id;
|
||||
private int windowId, transactionId;
|
||||
private boolean accepted;
|
||||
|
||||
@Override
|
||||
public void decode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_17) < 0) {
|
||||
windowId = byteBuf.readByte();
|
||||
id = byteBuf.readShort();
|
||||
transactionId = byteBuf.readShort();
|
||||
accepted = byteBuf.readBoolean();
|
||||
} else {
|
||||
id = byteBuf.readInt();
|
||||
transactionId = byteBuf.readInt();
|
||||
// Always set accepted to true since 1.17 or higher don't use
|
||||
// transactions for inventory confirmation anymore.
|
||||
accepted = true;
|
||||
@@ -54,12 +54,12 @@ public final class TransactionPacket implements FallbackPacket {
|
||||
public void encode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_17) < 0) {
|
||||
byteBuf.writeByte(windowId);
|
||||
byteBuf.writeShort((short) id);
|
||||
byteBuf.writeShort((short) transactionId);
|
||||
// The "accepted" field is actually really unnecessary since
|
||||
// it's never even used in the client.
|
||||
byteBuf.writeBoolean(accepted);
|
||||
} else {
|
||||
byteBuf.writeInt(id);
|
||||
byteBuf.writeInt(transactionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -26,21 +26,21 @@ import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.ProtocolUtil.writeString;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.writeVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeString;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class TransferPacket implements FallbackPacket {
|
||||
private String host;
|
||||
private int port;
|
||||
private String destinationHost;
|
||||
private int destinationPort;
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) {
|
||||
writeString(byteBuf, host);
|
||||
writeVarInt(byteBuf, port);
|
||||
writeString(byteBuf, destinationHost);
|
||||
writeVarInt(byteBuf, destinationPort);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+16
-27
@@ -26,15 +26,10 @@ import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.common.utility.component.ComponentHolder;
|
||||
|
||||
import java.time.Instant;
|
||||
import xyz.jonesdev.sonar.common.util.ComponentHolder;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.common.fallback.netty.FastUUID.EMPTY_UUID;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.ProtocolUtil.*;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.readVarInt;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.writeVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.*;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@@ -49,16 +44,9 @@ public final class UniversalChatPacket implements FallbackPacket {
|
||||
private ComponentHolder componentHolder;
|
||||
private String message;
|
||||
private byte type;
|
||||
private boolean signedPreview;
|
||||
private boolean unsigned = false;
|
||||
private Instant timestamp;
|
||||
private Instant expiry;
|
||||
private long salt;
|
||||
private boolean signed;
|
||||
private byte[] signature;
|
||||
|
||||
public UniversalChatPacket(final @NotNull Component component,
|
||||
final byte type) {
|
||||
public UniversalChatPacket(final @NotNull Component component, final byte type) {
|
||||
this(new ComponentHolder(component), type);
|
||||
}
|
||||
|
||||
@@ -92,28 +80,28 @@ public final class UniversalChatPacket implements FallbackPacket {
|
||||
public void decode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) {
|
||||
message = readString(byteBuf, 256);
|
||||
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19) >= 0) {
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19_1) <= 0) {
|
||||
final long expiresAt = byteBuf.readLong();
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19) >= 0) {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_1) <= 0) {
|
||||
byteBuf.readLong(); // expiresAt
|
||||
final long saltLong = byteBuf.readLong();
|
||||
final byte[] signatureBytes = readByteArray(byteBuf);
|
||||
boolean unsigned = false;
|
||||
|
||||
if (saltLong != 0L && signatureBytes.length > 0) {
|
||||
signature = signatureBytes;
|
||||
expiry = Instant.ofEpochMilli(expiresAt);
|
||||
} else if ((protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0
|
||||
} else if ((protocolVersion.compareTo(MINECRAFT_1_19_1) >= 0
|
||||
|| saltLong == 0L) && signatureBytes.length == 0) {
|
||||
unsigned = true;
|
||||
} else {
|
||||
throw new CorruptedFrameException("Invalid signature");
|
||||
}
|
||||
|
||||
signedPreview = byteBuf.readBoolean();
|
||||
final boolean signedPreview = byteBuf.readBoolean();
|
||||
if (signedPreview && unsigned) {
|
||||
throw new CorruptedFrameException("Signature missing");
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_1) >= 0) {
|
||||
final int size = readVarInt(byteBuf);
|
||||
if (size < 0 || size > 5) {
|
||||
throw new CorruptedFrameException("Invalid previous messages");
|
||||
@@ -130,11 +118,12 @@ public final class UniversalChatPacket implements FallbackPacket {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
timestamp = Instant.ofEpochMilli(byteBuf.readLong());
|
||||
salt = byteBuf.readLong();
|
||||
signed = byteBuf.readBoolean();
|
||||
byteBuf.readLong(); // timestamp
|
||||
byteBuf.readLong(); // salt
|
||||
final boolean signed = byteBuf.readBoolean();
|
||||
|
||||
if (signed) {
|
||||
byte[] sign = new byte[256];
|
||||
final byte[] sign = new byte[256];
|
||||
byteBuf.readBytes(sign);
|
||||
signature = sign;
|
||||
} else {
|
||||
@@ -142,7 +131,7 @@ public final class UniversalChatPacket implements FallbackPacket {
|
||||
}
|
||||
|
||||
readVarInt(byteBuf);
|
||||
byte[] bytes = new byte[DIV_FLOOR];
|
||||
final byte[] bytes = new byte[DIV_FLOOR];
|
||||
byteBuf.readBytes(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
+12
-12
@@ -25,19 +25,19 @@ import lombok.ToString;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.block.ChangedBlock;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.block.BlockUpdate;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.writeVarInt;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.writeVarLong;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarLong;
|
||||
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString(exclude = {"changedBlocks"})
|
||||
@ToString(exclude = {"blockUpdates"})
|
||||
public final class UpdateSectionBlocksPacket implements FallbackPacket {
|
||||
private int sectionX, sectionZ;
|
||||
private ChangedBlock[] changedBlocks;
|
||||
private BlockUpdate[] blockUpdates;
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
@@ -46,13 +46,13 @@ public final class UpdateSectionBlocksPacket implements FallbackPacket {
|
||||
byteBuf.writeInt(sectionZ);
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_8) < 0) {
|
||||
byteBuf.writeShort(changedBlocks.length);
|
||||
byteBuf.writeInt(4 * changedBlocks.length);
|
||||
byteBuf.writeShort(blockUpdates.length);
|
||||
byteBuf.writeInt(4 * blockUpdates.length);
|
||||
} else {
|
||||
writeVarInt(byteBuf, changedBlocks.length);
|
||||
writeVarInt(byteBuf, blockUpdates.length);
|
||||
}
|
||||
|
||||
for (final ChangedBlock block : changedBlocks) {
|
||||
for (final BlockUpdate block : blockUpdates) {
|
||||
byteBuf.writeShort(block.getLegacyBlockState());
|
||||
final int blockId = block.getType().getId(protocolVersion);
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13) >= 0) {
|
||||
@@ -68,7 +68,7 @@ public final class UpdateSectionBlocksPacket implements FallbackPacket {
|
||||
}
|
||||
} else {
|
||||
// We only need one Y position
|
||||
final int sectionY = changedBlocks[0].getPosition().getY() >> 4;
|
||||
final int sectionY = blockUpdates[0].getPosition().getY() >> 4;
|
||||
|
||||
// https://wiki.vg/Protocol#Update_Section_Blocks
|
||||
byteBuf.writeLong(((sectionX & 0x3FFFFFL) << 42) | (sectionY & 0xFFFFF) | ((sectionZ & 0x3FFFFFL) << 20));
|
||||
@@ -78,9 +78,9 @@ public final class UpdateSectionBlocksPacket implements FallbackPacket {
|
||||
byteBuf.writeBoolean(true); // suppress light updates
|
||||
}
|
||||
|
||||
writeVarInt(byteBuf, changedBlocks.length);
|
||||
writeVarInt(byteBuf, blockUpdates.length);
|
||||
|
||||
for (final ChangedBlock block : changedBlocks) {
|
||||
for (final BlockUpdate block : blockUpdates) {
|
||||
// https://wiki.vg/Protocol#Update_Section_Blocks
|
||||
final int shiftedBlockId = block.getType().getId(protocolVersion) << 12;
|
||||
final long positionIdValue = shiftedBlockId | block.getBlockState();
|
||||
|
||||
+3
-3
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.utility.component;
|
||||
package xyz.jonesdev.sonar.common.util;
|
||||
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.internal.LazilyParsedNumber;
|
||||
@@ -31,8 +31,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.ProtocolUtil.writeNamelessCompoundTag;
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.ProtocolUtil.writeString;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeNamelessCompoundTag;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeString;
|
||||
|
||||
// Mostly taken from
|
||||
// https://github.com/PaperMC/Velocity/blob/dev/3.0.0/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ComponentHolder.java
|
||||
+48
-17
@@ -15,50 +15,81 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.netty;
|
||||
package xyz.jonesdev.sonar.common.util;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 Jon Chambers
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
// Taken from
|
||||
// https://github.com/PaperMC/Velocity/blob/dev/3.0.0/api/src/main/java/com/velocitypowered/api/util/FastUuidSansHyphens.java
|
||||
public final class FastUUID {
|
||||
public static final UUID EMPTY_UUID = new UUID(0L, 0L);
|
||||
/**
|
||||
* This is a modified FastUUID implementation. The primary difference is that it does not dash its
|
||||
* UUIDs. As the native Java 9+ UUID.toString() implementation dashes its UUIDs, we use the FastUUID
|
||||
* methods, which ought to be faster than a String.replace().
|
||||
*/
|
||||
@UtilityClass
|
||||
public class FastUuidSansHyphens {
|
||||
private static final int MOJANG_BROKEN_UUID_LENGTH = 32;
|
||||
|
||||
private static final char[] HEX_DIGITS =
|
||||
new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
||||
|
||||
/**
|
||||
* Returns a string representation of the given UUID. The returned string is formatted as a
|
||||
* Mojang-style UUID.
|
||||
*
|
||||
* @param uuid the UUID to represent as a string
|
||||
*
|
||||
* @return a string representation of the given UUID
|
||||
*/
|
||||
public static String toString(final @NotNull UUID uuid) {
|
||||
public static @NotNull String toString(final @NotNull UUID uuid) {
|
||||
final long mostSignificantBits = uuid.getMostSignificantBits();
|
||||
final long leastSignificantBits = uuid.getLeastSignificantBits();
|
||||
|
||||
final char[] uuidChars = new char[MOJANG_BROKEN_UUID_LENGTH];
|
||||
|
||||
uuidChars[0] = HEX_DIGITS[(int) ((mostSignificantBits & 0xf000000000000000L) >>> 60)];
|
||||
uuidChars[1] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0f00000000000000L) >>> 56)];
|
||||
uuidChars[2] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00f0000000000000L) >>> 52)];
|
||||
uuidChars[3] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000f000000000000L) >>> 48)];
|
||||
uuidChars[4] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000f00000000000L) >>> 44)];
|
||||
uuidChars[5] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000f0000000000L) >>> 40)];
|
||||
uuidChars[6] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000f000000000L) >>> 36)];
|
||||
uuidChars[7] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000f00000000L) >>> 32)];
|
||||
uuidChars[8] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000f0000000L) >>> 28)];
|
||||
uuidChars[9] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000000f000000L) >>> 24)];
|
||||
uuidChars[0] = HEX_DIGITS[(int) ((mostSignificantBits & 0xf000000000000000L) >>> 60)];
|
||||
uuidChars[1] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0f00000000000000L) >>> 56)];
|
||||
uuidChars[2] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00f0000000000000L) >>> 52)];
|
||||
uuidChars[3] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000f000000000000L) >>> 48)];
|
||||
uuidChars[4] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000f00000000000L) >>> 44)];
|
||||
uuidChars[5] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000f0000000000L) >>> 40)];
|
||||
uuidChars[6] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000f000000000L) >>> 36)];
|
||||
uuidChars[7] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000f00000000L) >>> 32)];
|
||||
uuidChars[8] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000f0000000L) >>> 28)];
|
||||
uuidChars[9] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000000f000000L) >>> 24)];
|
||||
uuidChars[10] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000000f00000L) >>> 20)];
|
||||
uuidChars[11] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000000f0000L) >>> 16)];
|
||||
uuidChars[12] = HEX_DIGITS[(int) ((mostSignificantBits & 0x000000000000f000L) >>> 12)];
|
||||
uuidChars[13] = HEX_DIGITS[(int) ((mostSignificantBits & 0x0000000000000f00L) >>> 8)];
|
||||
uuidChars[14] = HEX_DIGITS[(int) ((mostSignificantBits & 0x00000000000000f0L) >>> 4)];
|
||||
uuidChars[15] = HEX_DIGITS[(int) (mostSignificantBits & 0x000000000000000fL)];
|
||||
uuidChars[15] = HEX_DIGITS[(int) (mostSignificantBits & 0x000000000000000fL)];
|
||||
uuidChars[16] = HEX_DIGITS[(int) ((leastSignificantBits & 0xf000000000000000L) >>> 60)];
|
||||
uuidChars[17] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0f00000000000000L) >>> 56)];
|
||||
uuidChars[18] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00f0000000000000L) >>> 52)];
|
||||
@@ -74,7 +105,7 @@ public final class FastUUID {
|
||||
uuidChars[28] = HEX_DIGITS[(int) ((leastSignificantBits & 0x000000000000f000L) >>> 12)];
|
||||
uuidChars[29] = HEX_DIGITS[(int) ((leastSignificantBits & 0x0000000000000f00L) >>> 8)];
|
||||
uuidChars[30] = HEX_DIGITS[(int) ((leastSignificantBits & 0x00000000000000f0L) >>> 4)];
|
||||
uuidChars[31] = HEX_DIGITS[(int) (leastSignificantBits & 0x000000000000000fL)];
|
||||
uuidChars[31] = HEX_DIGITS[(int) (leastSignificantBits & 0x000000000000000fL)];
|
||||
|
||||
return new String(uuidChars);
|
||||
}
|
||||
+2
-2
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.utility.geyser;
|
||||
package xyz.jonesdev.sonar.common.util;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.util.AttributeKey;
|
||||
@@ -28,7 +28,7 @@ import java.net.InetSocketAddress;
|
||||
* Simple utility to determine if someone joins using GeyserMC
|
||||
*/
|
||||
@UtilityClass
|
||||
public class GeyserUtil {
|
||||
public class GeyserDetection {
|
||||
// https://github.com/GeyserMC/Floodgate/blob/master/core/src/main/java/org/geysermc/floodgate/module/CommonModule.java#L206
|
||||
private final AttributeKey<Object> PLAYER_ATTRIBUTE = AttributeKey.valueOf("floodgate-player");
|
||||
|
||||
+127
-9
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.utility.protocol;
|
||||
package xyz.jonesdev.sonar.common.util;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufOutputStream;
|
||||
@@ -33,8 +33,6 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.readVarInt;
|
||||
|
||||
// Taken from
|
||||
// https://github.com/PaperMC/Velocity/blob/dev/3.0.0/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java
|
||||
@UtilityClass
|
||||
@@ -49,8 +47,128 @@ public class ProtocolUtil {
|
||||
private static final String UNREGISTER_CHANNEL = "minecraft:unregister";
|
||||
private static final Pattern INVALID_IDENTIFIER_REGEX = Pattern.compile("[^a-z0-9\\-_]*");
|
||||
|
||||
public static @NotNull UUID readUUID(final @NotNull ByteBuf byteBuf) {
|
||||
return new UUID(byteBuf.readLong(), byteBuf.readLong());
|
||||
public static int readVarInt(final ByteBuf byteBuf) {
|
||||
int read = readVarIntSafely(byteBuf);
|
||||
if (read == Integer.MIN_VALUE) {
|
||||
throw new CorruptedFrameException("Corrupt VarInt");
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
public static int readVarIntSafely(final @NotNull ByteBuf byteBuf) {
|
||||
int i = 0;
|
||||
int maxRead = Math.min(5, byteBuf.readableBytes());
|
||||
for (int j = 0; j < maxRead; j++) {
|
||||
int k = byteBuf.readByte();
|
||||
i |= (k & 0x7F) << j * 7;
|
||||
if ((k & 0x80) != 128) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
public static void writeVarInt(final ByteBuf byteBuf, final int value) {
|
||||
// Peel the one and two byte count cases explicitly as they are the most common VarInt sizes
|
||||
// that the proxy will write, to improve inlining.
|
||||
if ((value & (0xFFFFFFFF << 7)) == 0) {
|
||||
byteBuf.writeByte(value);
|
||||
} else if ((value & (0xFFFFFFFF << 14)) == 0) {
|
||||
int w = (value & 0x7F | 0x80) << 8 | (value >>> 7);
|
||||
byteBuf.writeShort(w);
|
||||
} else {
|
||||
writeVarIntFull(byteBuf, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeVarIntFull(final ByteBuf byteBuf, final int value) {
|
||||
// See https://steinborn.me/posts/performance/how-fast-can-you-write-a-varint/
|
||||
if ((value & (0xFFFFFFFF << 7)) == 0) {
|
||||
byteBuf.writeByte(value);
|
||||
} else if ((value & (0xFFFFFFFF << 14)) == 0) {
|
||||
int w = (value & 0x7F | 0x80) << 8 | (value >>> 7);
|
||||
byteBuf.writeShort(w);
|
||||
} else if ((value & (0xFFFFFFFF << 21)) == 0) {
|
||||
int w = (value & 0x7F | 0x80) << 16 | ((value >>> 7) & 0x7F | 0x80) << 8 | (value >>> 14);
|
||||
byteBuf.writeMedium(w);
|
||||
} else if ((value & (0xFFFFFFFF << 28)) == 0) {
|
||||
int w = (value & 0x7F | 0x80) << 24 | (((value >>> 7) & 0x7F | 0x80) << 16)
|
||||
| ((value >>> 14) & 0x7F | 0x80) << 8 | (value >>> 21);
|
||||
byteBuf.writeInt(w);
|
||||
} else {
|
||||
int w = (value & 0x7F | 0x80) << 24 | ((value >>> 7) & 0x7F | 0x80) << 16
|
||||
| ((value >>> 14) & 0x7F | 0x80) << 8 | ((value >>> 21) & 0x7F | 0x80);
|
||||
byteBuf.writeInt(w);
|
||||
byteBuf.writeByte(value >>> 28);
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeVarLong(final ByteBuf byteBuf, final long value) {
|
||||
// Peel the one and two byte count cases explicitly as they are the most common VarLong sizes
|
||||
// that the proxy will write, to improve inlining.
|
||||
if ((value & 0xFFFFFFFFFFFFFF80L) == 0L) {
|
||||
byteBuf.writeByte((byte) value);
|
||||
} else if ((value & 0xFFFFFFFFFFFFC000L) == 0L) {
|
||||
int w = (int) ((value & 0x7FL | 0x80L) << 8 | value >>> 7);
|
||||
byteBuf.writeShort(w);
|
||||
} else {
|
||||
writeVarLongFull(byteBuf, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeVarLongFull(final ByteBuf byteBuf, final long value) {
|
||||
if ((value & 0xFFFFFFFFFFFFFF80L) == 0L) {
|
||||
byteBuf.writeByte((byte) value);
|
||||
} else if ((value & 0xFFFFFFFFFFFFC000L) == 0L) {
|
||||
int w = (int) ((value & 0x7FL | 0x80L) << 8 | value >>> 7);
|
||||
byteBuf.writeShort(w);
|
||||
} else if ((value & 0xFFFFFFFFFFE00000L) == 0L) {
|
||||
int w = (int) ((value & 0x7FL | 0x80L) << 16 | (value >>> 7 & 0x7FL | 0x80L) << 8 | value >>> 14);
|
||||
byteBuf.writeMedium(w);
|
||||
} else if ((value & 0xFFFFFFFFF0000000L) == 0L) {
|
||||
int w =
|
||||
(int) ((value & 0x7FL | 0x80L) << 24 | (value >>> 7 & 0x7FL | 0x80L) << 16 | (value >>> 14 & 0x7FL | 0x80L) << 8 | value >>> 21);
|
||||
byteBuf.writeInt(w);
|
||||
} else {
|
||||
long l =
|
||||
(value & 0x7FL | 0x80L) << 24 | (value >>> 7 & 0x7FL | 0x80L) << 16 | (value >>> 14 & 0x7FL | 0x80L) << 8 | (value >>> 21 & 0x7FL | 0x80L);
|
||||
if ((value & 0xFFFFFFF800000000L) == 0L) {
|
||||
int w =
|
||||
(int) l;
|
||||
byteBuf.writeInt(w);
|
||||
byteBuf.writeByte((int) (value >>> 28));
|
||||
} else if ((value & 0xFFFFFC0000000000L) == 0L) {
|
||||
int w =
|
||||
(int) l;
|
||||
int w2 = (int) ((value >>> 28 & 0x7FL | 0x80L) << 8 | value >>> 35);
|
||||
byteBuf.writeInt(w);
|
||||
byteBuf.writeShort(w2);
|
||||
} else if ((value & 0xFFFE000000000000L) == 0L) {
|
||||
int w =
|
||||
(int) l;
|
||||
int w2 = (int) ((value >>> 28 & 0x7FL | 0x80L) << 16 | (value >>> 35 & 0x7FL | 0x80L) << 8 | value >>> 42);
|
||||
byteBuf.writeInt(w);
|
||||
byteBuf.writeMedium(w2);
|
||||
} else {
|
||||
long w =
|
||||
(value & 0x7FL | 0x80L) << 56 | (value >>> 7 & 0x7FL | 0x80L) << 48 | (value >>> 14 & 0x7FL | 0x80L) << 40 | (value >>> 21 & 0x7FL | 0x80L) << 32 | (value >>> 28 & 0x7FL | 0x80L) << 24 | (value >>> 35 & 0x7FL | 0x80L) << 16 | (value >>> 42 & 0x7FL | 0x80L) << 8 | value >>> 49;
|
||||
if ((value & 0xFF00000000000000L) == 0L) {
|
||||
byteBuf.writeLong(w);
|
||||
} else if ((value & Long.MIN_VALUE) == 0L) {
|
||||
byteBuf.writeLong(w);
|
||||
byteBuf.writeByte((byte) (value >>> 56));
|
||||
} else {
|
||||
int w2 = (int) ((value >>> 56 & 0x7FL | 0x80L) << 8 | value >>> 63);
|
||||
byteBuf.writeLong(w);
|
||||
byteBuf.writeShort(w2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void readUUID(final @NotNull ByteBuf byteBuf) {
|
||||
byteBuf.readLong(); // least
|
||||
byteBuf.readLong(); // most
|
||||
}
|
||||
|
||||
public static byte @NotNull [] readByteArray(final ByteBuf byteBuf) {
|
||||
@@ -82,7 +200,7 @@ public class ProtocolUtil {
|
||||
|
||||
public static @NotNull String readString(final ByteBuf byteBuf,
|
||||
final int cap) throws CorruptedFrameException {
|
||||
final int length = VarIntUtil.readVarInt(byteBuf);
|
||||
final int length = readVarInt(byteBuf);
|
||||
return readString(byteBuf, cap, length);
|
||||
}
|
||||
|
||||
@@ -100,7 +218,7 @@ public class ProtocolUtil {
|
||||
|
||||
public static void writeString(final ByteBuf byteBuf, final @NotNull CharSequence str) {
|
||||
final int size = ByteBufUtil.utf8Bytes(str);
|
||||
VarIntUtil.writeVarInt(byteBuf, size);
|
||||
writeVarInt(byteBuf, size);
|
||||
byteBuf.writeCharSequence(str, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@@ -118,12 +236,12 @@ public class ProtocolUtil {
|
||||
|
||||
public static void writeArray(final ByteBuf byteBuf, final byte @NotNull [] bytes) {
|
||||
checkFrame(bytes.length < Short.MAX_VALUE, "Too long array");
|
||||
VarIntUtil.writeVarInt(byteBuf, bytes.length);
|
||||
writeVarInt(byteBuf, bytes.length);
|
||||
byteBuf.writeBytes(bytes);
|
||||
}
|
||||
|
||||
public static void writeStringArray(final ByteBuf byteBuf, final String @NotNull [] stringArray) {
|
||||
VarIntUtil.writeVarInt(byteBuf, stringArray.length);
|
||||
writeVarInt(byteBuf, stringArray.length);
|
||||
for (final String s : stringArray) {
|
||||
writeString(byteBuf, s);
|
||||
}
|
||||
-145
@@ -1,145 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2024 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.common.utility.protocol;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.CorruptedFrameException;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@UtilityClass
|
||||
public class VarIntUtil {
|
||||
public static int readVarInt(final ByteBuf byteBuf) {
|
||||
int read = readVarIntSafely(byteBuf);
|
||||
if (read == Integer.MIN_VALUE) {
|
||||
throw new CorruptedFrameException("Corrupt VarInt");
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
public static int readVarIntSafely(final @NotNull ByteBuf byteBuf) {
|
||||
int i = 0;
|
||||
int maxRead = Math.min(5, byteBuf.readableBytes());
|
||||
for (int j = 0; j < maxRead; j++) {
|
||||
int k = byteBuf.readByte();
|
||||
i |= (k & 0x7F) << j * 7;
|
||||
if ((k & 0x80) != 128) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
public static void writeVarInt(final ByteBuf byteBuf, final int value) {
|
||||
// Peel the one and two byte count cases explicitly as they are the most common VarInt sizes
|
||||
// that the proxy will write, to improve inlining.
|
||||
if ((value & (0xFFFFFFFF << 7)) == 0) {
|
||||
byteBuf.writeByte(value);
|
||||
} else if ((value & (0xFFFFFFFF << 14)) == 0) {
|
||||
int w = (value & 0x7F | 0x80) << 8 | (value >>> 7);
|
||||
byteBuf.writeShort(w);
|
||||
} else {
|
||||
writeVarIntFull(byteBuf, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeVarIntFull(final ByteBuf byteBuf, final int value) {
|
||||
// See https://steinborn.me/posts/performance/how-fast-can-you-write-a-varint/
|
||||
if ((value & (0xFFFFFFFF << 7)) == 0) {
|
||||
byteBuf.writeByte(value);
|
||||
} else if ((value & (0xFFFFFFFF << 14)) == 0) {
|
||||
int w = (value & 0x7F | 0x80) << 8 | (value >>> 7);
|
||||
byteBuf.writeShort(w);
|
||||
} else if ((value & (0xFFFFFFFF << 21)) == 0) {
|
||||
int w = (value & 0x7F | 0x80) << 16 | ((value >>> 7) & 0x7F | 0x80) << 8 | (value >>> 14);
|
||||
byteBuf.writeMedium(w);
|
||||
} else if ((value & (0xFFFFFFFF << 28)) == 0) {
|
||||
int w = (value & 0x7F | 0x80) << 24 | (((value >>> 7) & 0x7F | 0x80) << 16)
|
||||
| ((value >>> 14) & 0x7F | 0x80) << 8 | (value >>> 21);
|
||||
byteBuf.writeInt(w);
|
||||
} else {
|
||||
int w = (value & 0x7F | 0x80) << 24 | ((value >>> 7) & 0x7F | 0x80) << 16
|
||||
| ((value >>> 14) & 0x7F | 0x80) << 8 | ((value >>> 21) & 0x7F | 0x80);
|
||||
byteBuf.writeInt(w);
|
||||
byteBuf.writeByte(value >>> 28);
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeVarLong(final ByteBuf byteBuf, final long value) {
|
||||
// Peel the one and two byte count cases explicitly as they are the most common VarLong sizes
|
||||
// that the proxy will write, to improve inlining.
|
||||
if ((value & 0xFFFFFFFFFFFFFF80L) == 0L) {
|
||||
byteBuf.writeByte((byte) value);
|
||||
} else if ((value & 0xFFFFFFFFFFFFC000L) == 0L) {
|
||||
int w = (int) ((value & 0x7FL | 0x80L) << 8 | value >>> 7);
|
||||
byteBuf.writeShort(w);
|
||||
} else {
|
||||
writeVarLongFull(byteBuf, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeVarLongFull(final ByteBuf byteBuf, final long value) {
|
||||
if ((value & 0xFFFFFFFFFFFFFF80L) == 0L) {
|
||||
byteBuf.writeByte((byte) value);
|
||||
} else if ((value & 0xFFFFFFFFFFFFC000L) == 0L) {
|
||||
int w = (int) ((value & 0x7FL | 0x80L) << 8 | value >>> 7);
|
||||
byteBuf.writeShort(w);
|
||||
} else if ((value & 0xFFFFFFFFFFE00000L) == 0L) {
|
||||
int w = (int) ((value & 0x7FL | 0x80L) << 16 | (value >>> 7 & 0x7FL | 0x80L) << 8 | value >>> 14);
|
||||
byteBuf.writeMedium(w);
|
||||
} else if ((value & 0xFFFFFFFFF0000000L) == 0L) {
|
||||
int w =
|
||||
(int) ((value & 0x7FL | 0x80L) << 24 | (value >>> 7 & 0x7FL | 0x80L) << 16 | (value >>> 14 & 0x7FL | 0x80L) << 8 | value >>> 21);
|
||||
byteBuf.writeInt(w);
|
||||
} else {
|
||||
long l =
|
||||
(value & 0x7FL | 0x80L) << 24 | (value >>> 7 & 0x7FL | 0x80L) << 16 | (value >>> 14 & 0x7FL | 0x80L) << 8 | (value >>> 21 & 0x7FL | 0x80L);
|
||||
if ((value & 0xFFFFFFF800000000L) == 0L) {
|
||||
int w =
|
||||
(int) l;
|
||||
byteBuf.writeInt(w);
|
||||
byteBuf.writeByte((int) (value >>> 28));
|
||||
} else if ((value & 0xFFFFFC0000000000L) == 0L) {
|
||||
int w =
|
||||
(int) l;
|
||||
int w2 = (int) ((value >>> 28 & 0x7FL | 0x80L) << 8 | value >>> 35);
|
||||
byteBuf.writeInt(w);
|
||||
byteBuf.writeShort(w2);
|
||||
} else if ((value & 0xFFFE000000000000L) == 0L) {
|
||||
int w =
|
||||
(int) l;
|
||||
int w2 = (int) ((value >>> 28 & 0x7FL | 0x80L) << 16 | (value >>> 35 & 0x7FL | 0x80L) << 8 | value >>> 42);
|
||||
byteBuf.writeInt(w);
|
||||
byteBuf.writeMedium(w2);
|
||||
} else {
|
||||
long w =
|
||||
(value & 0x7FL | 0x80L) << 56 | (value >>> 7 & 0x7FL | 0x80L) << 48 | (value >>> 14 & 0x7FL | 0x80L) << 40 | (value >>> 21 & 0x7FL | 0x80L) << 32 | (value >>> 28 & 0x7FL | 0x80L) << 24 | (value >>> 35 & 0x7FL | 0x80L) << 16 | (value >>> 42 & 0x7FL | 0x80L) << 8 | value >>> 49;
|
||||
if ((value & 0xFF00000000000000L) == 0L) {
|
||||
byteBuf.writeLong(w);
|
||||
} else if ((value & Long.MIN_VALUE) == 0L) {
|
||||
byteBuf.writeLong(w);
|
||||
byteBuf.writeByte((byte) (value >>> 56));
|
||||
} else {
|
||||
int w2 = (int) ((value >>> 56 & 0x7FL | 0x80L) << 8 | value >>> 63);
|
||||
byteBuf.writeLong(w);
|
||||
byteBuf.writeShort(w2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,39 +105,51 @@ verification:
|
||||
# - DURING_ATTACK: Noví hráči dostanou map captchu jenom při útoku
|
||||
# - NEVER: Noví hráči nikdy nedostanou map CAPTCHU (Doporučeno)
|
||||
timing: NEVER
|
||||
# Možnosti efektů na obrázek zobrazený přehrávači
|
||||
# Možnosti pro efekty na zobrazeném obrázku
|
||||
# Toto byste měli upravovat, pouze pokud víte, co děláte
|
||||
effects:
|
||||
# Přidá do obrázku efekt záře
|
||||
flare: true
|
||||
# Přidá do obrázku náhodné čáry
|
||||
scratches: true
|
||||
# Přidá filtr zvlnění (sinus) zkreslení
|
||||
# Přidá filtr zvlnění (vlnitý efekt)
|
||||
ripple: true
|
||||
# Přidá horizontální filtr zkreslení
|
||||
smear: true
|
||||
# Dodává obrazu trochu rozmazání/zkreslení
|
||||
pinch: true
|
||||
# Použije korekci barev (sytost barev)
|
||||
saturation: 0.15
|
||||
# Přidá horizontální trojúhelníkový filtr zvlnění
|
||||
distortion: 2
|
||||
# Přidá nárazový filtr (zaostřený text)
|
||||
bump: true
|
||||
# Filtr zkreslení
|
||||
distortion:
|
||||
# Měl by Sonar použít filtr zkreslení?
|
||||
enabled: true
|
||||
# Jaký tvar by měl Sonar použít pro filtr zkreslení?
|
||||
# Typy: 0, 1, 2, 3
|
||||
# - 0: Kříže
|
||||
# - 1: Čáry
|
||||
# - 2: Kruhy
|
||||
# - 3: Čtverce
|
||||
shape: 2
|
||||
# Vzdálenost mezi zkreslenými pixely/objekty
|
||||
distance: 5
|
||||
# Hustota zkreslených pixelů/objektů
|
||||
density: 0.1
|
||||
# Míra prolnutí mezi barvami objektů
|
||||
mix: 0.15
|
||||
# Cesta k obrázku na pozadí, který Sonar používá pro CAPTCHA
|
||||
# Pokud není obrázek nalezen, bude jako výchozí pozadí použit šum
|
||||
# Zde můžete jednoduše zadat název souboru, pokud je soubor v adresáři pluginu
|
||||
background: ''
|
||||
# Měl by Sonar automaticky detekovat barvu pozadí?
|
||||
# To se provádí za účelem vytvoření kontrastu mezi obrázkem na pozadí a textem
|
||||
# Pokud je toto zakázáno, Sonar použije pro barvy náhodný přechod
|
||||
auto-color: false
|
||||
# Kolik odpovědí by měl Sonar předgenerovat?
|
||||
# Tento úkol běží souběžně v pozadí
|
||||
precompute: 500
|
||||
# Jak dlouho by měl Sonar čekat než hráč selže CAPTCHU?
|
||||
# (Hodnota udávaná v milisekundách: 1 sekunda = 1000 milisekund)
|
||||
max-duration: 45000
|
||||
max-duration: 30000
|
||||
# Kolikrát musí hráč selhat CAPTCHU před celkovém selhání verifikace?
|
||||
max-tries: 3
|
||||
# Znaky (čísla a písmena), které jsou povoleny k zobrazování v CAPTCHE
|
||||
# Nedoporučuje se přidávat čísla nebo písmena, která by se mohla navzájem podobat
|
||||
dictionary: '1235689'
|
||||
# Jaké typy fontů by měl Sonar používat pro map CAPTCHU?
|
||||
font-names:
|
||||
- Dialog
|
||||
- DialogInput
|
||||
- SansSerif
|
||||
- Monospaced
|
||||
|
||||
# Zkouší pokuď hráč posílá správný "client brand packet" serveru
|
||||
client-brand:
|
||||
|
||||
@@ -102,39 +102,51 @@ verification:
|
||||
# - DURING_ATTACK: Neue Spieler erhalten nur während eines Angriffs ein CAPTCHA
|
||||
# - NEVER: Neue Spieler erhalten nie ein CAPTCHA (Empfohlen)
|
||||
timing: NEVER
|
||||
# Optionen für die Effekte auf das dem Player angezeigte Bild
|
||||
# Optionen für die Effekte auf dem angezeigten Bild
|
||||
# Sie sollten dies nur bearbeiten, wenn Sie wissen, was Sie tun
|
||||
effects:
|
||||
# Fügt dem Bild einen Leuchteffekt hinzu
|
||||
flare: true
|
||||
# Fügt dem Bild zufällige Linien hinzu
|
||||
scratches: true
|
||||
# Wendet einen Sinus-Verzerrungsfilter an
|
||||
# Wendet einen Ripple-Filter an (Welleneffekt)
|
||||
ripple: true
|
||||
# Wendet einen horizontalen Verzerrungsfilter an
|
||||
smear: true
|
||||
# Fügt dem Bild etwas Unschärfe/Verzerrung hinzu
|
||||
pinch: true
|
||||
# Wendet eine Farbkorrektur an (Farbsättigung)
|
||||
saturation: 0.15
|
||||
# Wendet einen horizontalen dreieckigen Wellenfilter an
|
||||
distortion: 2
|
||||
# Wendet einen Bump-Filter an (geschärfter Text)
|
||||
bump: true
|
||||
# Verzerrungsfilter
|
||||
distortion:
|
||||
# Sollte Sonar einen Verzerrungsfilter anwenden?
|
||||
enabled: true
|
||||
# Welche Form sollte Sonar für den Verzerrungsfilter verwenden?
|
||||
# Mögliche Typen: 0, 1, 2, 3
|
||||
# - 0: Kreuze
|
||||
# - 1: Linien
|
||||
# - 2: Kreise
|
||||
# - 3: Quadrate
|
||||
shape: 2
|
||||
# Abstand zwischen verzerrten Pixeln/Objekten
|
||||
distance: 5
|
||||
# Dichte der verzerrten Pixel/Objekte
|
||||
density: 0.1
|
||||
# Ausmaß der Vermischung zwischen den Farben von Objekten
|
||||
mix: 0.15
|
||||
# Pfad zum Hintergrundbild, das Sonar für das CAPTCHA verwendet
|
||||
# Wenn das Bild nicht gefunden wird, wird ein Rauschen als Standardhintergrund verwendet
|
||||
# Sie können hier einfach den Namen der Datei eingeben, wenn sich die Datei im Plugin-Verzeichnis befindet
|
||||
background: ''
|
||||
# Sollte Sonar die Hintergrundfarbe automatisch erkennen?
|
||||
# Dies geschieht, um einen Kontrast zwischen dem Hintergrundbild und dem Text zu erzeugen
|
||||
# Wenn dies deaktiviert ist, verwendet Sonar einen zufälligen Farbverlauf für die Farben
|
||||
auto-color: false
|
||||
# Wie viele Antworten soll Sonar vorbereiten?
|
||||
# Dieser Vorgang erfolgt asynchron im Hintergrund
|
||||
precompute: 500
|
||||
# Wie lange soll Sonar warten, bis der Spieler das CAPTCHA nicht besteht?
|
||||
# (Dieser Wert stellt die Zeit in Millisekunden dar: 1 Sekunde = 1000 Millisekunden)
|
||||
max-duration: 45000
|
||||
max-duration: 30000
|
||||
# Wie oft muss ein Spieler das CAPTCHA nicht bestehen, bevor er die Überprüfung nicht besteht?
|
||||
max-tries: 3
|
||||
# Zeichen (Buchstaben und Zahlen), die im Antwort-CAPTCHA erscheinen dürfen
|
||||
# Es wird nicht empfohlen, Zahlen oder Buchstaben hinzuzufügen, die einander ähneln könnten
|
||||
dictionary: '1235689'
|
||||
# Welche Schriftarten soll Sonar für die Map-CAPTCHA-Codes verwenden?
|
||||
font-names:
|
||||
- Dialog
|
||||
- DialogInput
|
||||
- SansSerif
|
||||
- Monospaced
|
||||
|
||||
# Überprüft, ob die Spieler eine gültige Client-Marke an den Server senden
|
||||
client-brand:
|
||||
|
||||
@@ -105,38 +105,50 @@ verification:
|
||||
# - NEVER: New players will never receive a CAPTCHA (Recommended)
|
||||
timing: NEVER
|
||||
# Options for the effects on the image shown to the player
|
||||
# You should only edit this if you know what you're doing
|
||||
effects:
|
||||
# Adds a glow effect to the image
|
||||
flare: true
|
||||
# Adds random lines to the image
|
||||
scratches: true
|
||||
# Applies a ripple (sine) distortion filter
|
||||
# Applies a ripple filter (wavy effect)
|
||||
ripple: true
|
||||
# Applies a horizontal distortion filter
|
||||
smear: true
|
||||
# Adds a bit of blurriness/distortion to the image
|
||||
pinch: true
|
||||
# Applies color correction (color saturation)
|
||||
saturation: 0.15
|
||||
# Applies a horizontal triangular ripple filter
|
||||
distortion: 2
|
||||
# Applies a bump filter (sharpened text)
|
||||
bump: true
|
||||
# Distortion filter
|
||||
distortion:
|
||||
# Should Sonar apply a distortion filter?
|
||||
enabled: true
|
||||
# What shape should Sonar use for the distortion filter?
|
||||
# Possible types: 0, 1, 2, 3
|
||||
# - 0: Crosses
|
||||
# - 1: Lines
|
||||
# - 2: Circles
|
||||
# - 3: Squares
|
||||
shape: 2
|
||||
# Distance between distorted pixels/objects
|
||||
distance: 5
|
||||
# Density of the distorted pixels/objects
|
||||
density: 0.1
|
||||
# Amount of blending between the colors of objects
|
||||
mix: 0.15
|
||||
# Path to the background image Sonar uses for the CAPTCHA
|
||||
# If the image isn't found, a noise will be used as the default background
|
||||
# You can simply put the name of the file here if file is in the plugin directory
|
||||
background: ''
|
||||
# Should Sonar automatically detect the background color?
|
||||
# This is done to create a contrast between the background image and the text
|
||||
# If this is disabled, Sonar will use a random gradient for the colors
|
||||
auto-color: false
|
||||
# How many answers should Sonar prepare?
|
||||
# This task happens asynchronously in the background
|
||||
precompute: 500
|
||||
# How long should Sonar wait until the player fails the CAPTCHA?
|
||||
# (This value represents the time in milliseconds: 1 second = 1000 milliseconds)
|
||||
max-duration: 45000
|
||||
max-duration: 30000
|
||||
# How many times must a player fail the CAPTCHA before failing the verification?
|
||||
max-tries: 3
|
||||
# Characters (letters and numbers) that are allowed to appear in the answer to the CAPTCHA
|
||||
# It is not recommended to add numbers or letters that might look like one another
|
||||
dictionary: '1235689'
|
||||
# Which fonts should Sonar use for the map CAPTCHA codes?
|
||||
font-names:
|
||||
- Dialog
|
||||
- DialogInput
|
||||
- SansSerif
|
||||
- Monospaced
|
||||
|
||||
# Checks if the players is sending a valid client brand to the server
|
||||
client-brand:
|
||||
|
||||
@@ -105,38 +105,49 @@ verification:
|
||||
# - NEVER: ახალ მოთამაშეებს არასდროს მოუწევთ CAPTCHA-ს გაკეთება (Recommended)
|
||||
timing: NEVER
|
||||
# Options for the effects on the image shown to the player
|
||||
# You should only edit this if you know what you're doing
|
||||
effects:
|
||||
# Adds a glow effect to the image
|
||||
flare: true
|
||||
# Adds random lines to the image
|
||||
scratches: true
|
||||
# Applies a ripple (sine) distortion filter
|
||||
# Applies a ripple filter (wavy effect)
|
||||
ripple: true
|
||||
# Applies a horizontal distortion filter
|
||||
smear: true
|
||||
# Adds a bit of blurriness/distortion to the image
|
||||
pinch: true
|
||||
# Applies color correction (color saturation)
|
||||
saturation: 0.15
|
||||
# Applies a horizontal triangular ripple filter
|
||||
distortion: 2
|
||||
# Applies a bump filter (sharpened text)
|
||||
bump: true
|
||||
# Distortion filter
|
||||
distortion:
|
||||
# Should Sonar apply a distortion filter?
|
||||
enabled: true
|
||||
# What shape should Sonar use for the distortion filter?
|
||||
# Possible types: 0, 1, 2, 3
|
||||
# - 0: Crosses
|
||||
# - 1: Lines
|
||||
# - 2: Circles
|
||||
# - 3: Squares
|
||||
shape: 2
|
||||
# Distance between distorted pixels/objects
|
||||
distance: 5
|
||||
# Density of the distorted pixels/objects
|
||||
density: 0.1
|
||||
# Amount of blending between the colors of objects
|
||||
mix: 0.15
|
||||
# Path to the background image Sonar uses for the CAPTCHA
|
||||
# If the image isn't found, a noise will be used as the default background
|
||||
background: ''
|
||||
# Should Sonar automatically detect the background color?
|
||||
# This is done to create a contrast between the background image and the text
|
||||
# You can simply put the name of the file here if file is in the plugin directory
|
||||
auto-color: false
|
||||
# რამდენი პასუხი უნდა მოამზადოს Sonar-მა?
|
||||
# ეს ხდება ასინქრონულად უკანა ფონზე
|
||||
precompute: 500
|
||||
# რამდენ ხანს უნდა დაელოდოს Sonar-ი, სანამ მოთამაშე ვერ გააკეთებს წარმატებულად CAPTCHA-ს?
|
||||
# (ეს არის ნაჩვენები მილიწამებში: 1 წამი = 1000 მილიწამი)
|
||||
max-duration: 45000
|
||||
max-duration: 30000
|
||||
# რამდენჯერ ვერ უდნა გაიაროს CAPTCHA მოთამაშემ სანამ ვერიფიკაციას ვერ გაივლის?
|
||||
max-tries: 3
|
||||
# სიმბოლოები (ასოები და რიცხვები), რომლებიც დაშვებულია გამოჩნდეს CAPTCHA-ს პასუხში
|
||||
# It is not recommended to add numbers or letters that might look like one another
|
||||
dictionary: '1235689'
|
||||
# Which fonts should Sonar use for the map CAPTCHA codes?
|
||||
font-names:
|
||||
- Dialog
|
||||
- DialogInput
|
||||
- SansSerif
|
||||
- Monospaced
|
||||
|
||||
#ამოწმებს, აგზავნიან თუ არა მოთამაშეები სერვერზე რეალურ კლიენტის სახელს
|
||||
client-brand:
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
#
|
||||
# /$$$$$$
|
||||
# /$$__ $$
|
||||
# | $$ \__/ /$$$$$$ /$$$$$$$ /$$$$$$ /$$$$$$
|
||||
# | $$$$$$ /$$__ $$| $$__ $$ |____ $$ /$$__ $$
|
||||
# \____ $$| $$ \ $$| $$ \ $$ /$$$$$$$| $$ \__/
|
||||
# /$$ \ $$| $$ | $$| $$ | $$ /$$__ $$| $$
|
||||
# | $$$$$$/| $$$$$$/| $$ | $$| $$$$$$$| $$
|
||||
# \______/ \______/ |__/ |__/ \_______/|__/
|
||||
#
|
||||
# Hulp nodig of vragen? https://jonesdev.xyz/discord
|
||||
# Broncode: https://github.com/jonesdevelopment/sonar
|
||||
# Copyright Jones Development and Sonar Contributors, licensed under GPL-3.0
|
||||
#
|
||||
|
||||
# Algemene instellingen voor Sonar
|
||||
general:
|
||||
# Moet Sonar de IP-adressen van spelers in de console registreren?
|
||||
log-player-addresses: true
|
||||
# Maximaal aantal spelers online met hetzelfde IP-adres
|
||||
max-online-per-ip: 3
|
||||
|
||||
# Gevoeligheidsinstellingen voor de aanvalstracker
|
||||
# Bewerk dit alleen als je weet wat je doet
|
||||
attack-tracker:
|
||||
# Minimumaantal nieuwe spelers om een aanval te kunnen detecteren
|
||||
min-players-for-attack: 8
|
||||
# De hoeveelheid tijd die moet verstrijken voordat een aanval voorbij is
|
||||
# (Deze waarde vertegenwoordigt de tijd in milliseconden: 1 seconde = 1000 milliseconden)
|
||||
min-attack-duration: 30000
|
||||
# Aantal keren dat een incident moet worden gemeld om als aanval te worden erkend
|
||||
# Dit nummer fungeert als buffer om valse aanvalsmeldingen eruit te filteren
|
||||
min-attack-threshold: 2
|
||||
# De hoeveelheid tijd die moet verstrijken voordat een nieuwe aanval wordt gedetecteerd
|
||||
# (Deze waarde vertegenwoordigt de tijd in milliseconden: 1 seconde = 1000 milliseconden)
|
||||
attack-cooldown-delay: 3000
|
||||
|
||||
# U kunt Sonar verbinden met een database om geverifieerde spelers te behouden, zelfs nadat u uw server opnieuw hebt opgestart
|
||||
# Let op: IP-adressen worden opgeslagen in platte tekst. U bent verantwoordelijk voor het veilig houden van uw database!
|
||||
database:
|
||||
# Type database dat Sonar gebruikt om geverifieerde spelers op te slaan
|
||||
# Mogelijke typen: NONE, MYSQL, MARIADB
|
||||
type: NONE
|
||||
# Adres voor authenticatie met de SQL-database
|
||||
host: localhost
|
||||
# Poort voor authenticatie met de SQL-database
|
||||
port: 3306
|
||||
# Naam van de SQL-database
|
||||
name: sonar
|
||||
# Gebruikersnaam voor authenticatie met de SQL-database
|
||||
username: ''
|
||||
# Wachtwoord voor authenticatie met de SQL-database
|
||||
password: ''
|
||||
# Hoeveel dagen moet Sonar geverifieerde spelers in de database bewaren?
|
||||
maximum-age: 5
|
||||
|
||||
# Elk nieuw inlogverzoek wordt in de wachtrij geplaatst om spam-join-aanvallen te voorkomen
|
||||
# De wachtrij wordt elke seconde bijgewerkt (20 ticks)
|
||||
queue:
|
||||
# Maximaal aantal gelijktijdige peilingen per seconde
|
||||
max-polls: 30
|
||||
|
||||
# Elke nieuwe speler die voor de eerste keer meedoet, wordt naar gestuurd
|
||||
# een lichtgewicht limbo-server waar geavanceerde bot-controles worden uitgevoerd
|
||||
verification:
|
||||
# Wanneer moet Sonar nieuwe spelers verifiëren?
|
||||
# Mogelijke typen: ALWAYS, DURING_ATTACK, NEVER
|
||||
# - ALWAYS: Nieuwe spelers worden altijd gecontroleerd (Aanbevolen)
|
||||
# - DURING_ATTACK: Nieuwe spelers worden alleen gecontroleerd tijdens een aanval
|
||||
# - NEVER: Nieuwe spelers worden nooit gecontroleerd
|
||||
timing: ALWAYS
|
||||
checks:
|
||||
# Controleert of de valbeweging van de spelers de zwaartekrachtformule van Minecraft volgt
|
||||
# Alle voorspelde bewegingen worden vooraf berekend om prestaties te besparen
|
||||
gravity:
|
||||
# Moet Sonar controleren op geldige zwaartekracht van de cliënt? (Aanbevolen)
|
||||
enabled: true
|
||||
# Moet Sonar controleren of de speler tegen blokken botst? (Aanbevolen)
|
||||
check-collisions: true
|
||||
# Moet Sonar de speler een CAPTCHA laten oplossen als hij niet slaagt voor de zwaartekracht controle?
|
||||
captcha-on-fail: false
|
||||
# Maximaal aantal ticks dat de speler moet laten vallen om het platform te mogen raken
|
||||
max-movement-ticks: 8
|
||||
# De spelmodus van de speler tijdens verificatie
|
||||
# Mogelijke typen: SURVIVAL, CREATIVE, ADVENTURE
|
||||
# - SURVIVAL: alle UI-componenten zijn zichtbaar
|
||||
# - CREATIVE: gezondheid en honger zijn verborgen
|
||||
# - ADVENTURE: alle UI-componenten zijn zichtbaar
|
||||
gamemode: ADVENTURE
|
||||
|
||||
# Controleert of de klant de juiste pakketten verzendt bij het monteren van een voertuig
|
||||
vehicle:
|
||||
# Mogelijke typen: ALWAYS, DURING_ATTACK, NEVER
|
||||
# - ALWAYS: Nieuwe spelers worden altijd op een voertuig gemonteerd (Aanbevolen)
|
||||
# - DURING_ATTACK: Nieuwe spelers worden alleen tijdens een aanval op een voertuig gemonteerd
|
||||
# - NEVER: Nieuwe spelers zullen nooit op een voertuig worden gemonteerd
|
||||
timing: ALWAYS
|
||||
|
||||
# Laat de speler een code van een virtuele kaart in de chat typen
|
||||
map-captcha:
|
||||
# Wanneer moet Sonar de speler een CAPTCHA laten oplossen?
|
||||
# Mogelijke typen: ALWAYS, DURING_ATTACK, NEVER
|
||||
# - ALWAYS: Nieuwe spelers ontvangen altijd een CAPTCHA
|
||||
# - DURING_ATTACK: Nieuwe spelers ontvangen alleen een CAPTCHA tijdens een aanval
|
||||
# - NEVER: Nieuwe spelers zullen nooit een CAPTCHA ontvangen (Aanbevolen)
|
||||
timing: NEVER
|
||||
# Opties voor de effecten op de afbeelding die aan de speler wordt getoond
|
||||
# Je moet dit alleen bewerken als je weet wat je doet
|
||||
effects:
|
||||
# Voegt willekeurige lijnen toe aan de afbeelding
|
||||
scratches: true
|
||||
# Past een ripple-filter toe (golfeffect)
|
||||
ripple: true
|
||||
# Past een bump-filter toe (verscherpte tekst)
|
||||
bump: true
|
||||
# Vervormingsfilter
|
||||
distortion:
|
||||
# Moet Sonar een vervormingsfilter toepassen?
|
||||
enabled: true
|
||||
# Welke vorm moet Sonar gebruiken voor het vervormingsfilter?
|
||||
# Mogelijke typen: 0, 1, 2, 3
|
||||
# - 0: Kruisen
|
||||
# - 1: Lijnen
|
||||
# - 2: Cirkels
|
||||
# - 3: Vierkantjes
|
||||
shape: 2
|
||||
# Afstand tussen vervormde pixels/objecten
|
||||
distance: 5
|
||||
# Dichtheid van de vervormde pixels/objecten
|
||||
density: 0.1
|
||||
# Hoeveelheid overvloeiing tussen de kleuren van objecten
|
||||
mix: 0.15
|
||||
# Pad naar de achtergrondafbeelding die Sonar gebruikt voor de CAPTCHA
|
||||
# Als de afbeelding niet wordt gevonden, wordt er een ruis gebruikt als standaardachtergrond
|
||||
# U kunt eenvoudig de naam van het bestand hier plaatsen als het bestand zich in de plugin map bevindt
|
||||
background: ''
|
||||
# Moet Sonar automatisch de achtergrondkleur detecteren?
|
||||
# Dit wordt gedaan om een contrast te creëren tussen de achtergrondafbeelding en de tekst
|
||||
# Als dit is uitgeschakeld, gebruikt Sonar een willekeurig verloop voor de kleuren
|
||||
auto-color: false
|
||||
# Hoeveel antwoorden moet Sonar voorbereiden?
|
||||
# Deze taak gebeurt asynchroon op de achtergrond
|
||||
precompute: 500
|
||||
# Hoe lang moet Sonar wachten totdat de speler de CAPTCHA niet haalt?
|
||||
# (Deze waarde vertegenwoordigt de tijd in milliseconden: 1 seconde = 1000 milliseconden)
|
||||
max-duration: 30000
|
||||
# Hoe vaak moet een speler de CAPTCHA niet halen voordat de verificatie mislukt?
|
||||
max-tries: 3
|
||||
# Tekens (letters en cijfers) die mogen voorkomen in het antwoord op de CAPTCHA
|
||||
# Het wordt niet aanbevolen om cijfers of letters toe te voegen die op elkaar lijken
|
||||
dictionary: '1235689'
|
||||
|
||||
# Controleert of de speler een geldig Minecraft-klantmerk naar de server verzendt
|
||||
client-brand:
|
||||
# Moet Sonar controleren op geldige Minecraft-klantmerk? (Aanbevolen)
|
||||
enabled: true
|
||||
# Regex voor het valideren van Minecraft-klantmerken tijdens verificatie
|
||||
valid-regex: ^[!-~ ]+$
|
||||
# Maximale lengte van het Minecraft-klantmerk tijdens verificatie
|
||||
max-length: 64
|
||||
|
||||
# Regex voor het valideren van gebruikersnamen tijdens verificatie
|
||||
valid-name-regex: ^[a-zA-Z0-9_]+$
|
||||
# Regex voor het valideren van de landinstelling van de client tijdens verificatie
|
||||
valid-locale-regex: ^[a-zA-Z_]+$
|
||||
# Ping moet een speler hebben om een time-out te krijgen
|
||||
# (Deze waarde vertegenwoordigt de tijd in milliseconden: 1 seconde = 1000 milliseconden)
|
||||
max-ping: 10000
|
||||
# Maximaal aantal loginpakketten dat de speler moet verzenden om te worden geschopt
|
||||
max-login-packets: 256
|
||||
|
||||
# Indien ingeschakeld, wordt de speler teruggestuurd naar de oorspronkelijke server
|
||||
# nadat u de bot-verificatie met succes heeft doorstaan.
|
||||
# Deze functie is geïntroduceerd door Mojang in Minecraft versie 1.20.5
|
||||
transfer:
|
||||
# Moet Sonar de speler overbrengen naar de oorspronkelijke server (in plaats van hem te schoppen)?
|
||||
# Om dit te laten werken, moet u de functie inschakelen in de configuratie van uw server
|
||||
# Bovendien wilt u mogelijk de beperking van de inlegsnelheid verminderen
|
||||
# uitgevoerd door Velocity van andere proxy's/plugins, omdat dit de
|
||||
# speler wordt niet correct weergegeven.
|
||||
enabled: false
|
||||
# Naar welke server moet Sonar de speler overbrengen wanneer de verificatie is geslaagd?
|
||||
# Voer het server-IP in dat door spelers wordt gebruikt om normaal verbinding te maken met uw server.
|
||||
# U kunt bijvoorbeeld "mc.hypixel.net" of een direct IP-adres zoals "1.1.1.1" invoeren
|
||||
destination-host: "play.my-server.com"
|
||||
# Welke poort moet Sonar gebruiken bij het overbrengen van de speler naar de oorspronkelijke server?
|
||||
# Als uw server geen poort nodig heeft om verbinding mee te maken, kunt u dit op 25565 laten staan.
|
||||
destination-port: 25565
|
||||
|
||||
# Moet Sonar nieuwe verificatiepogingen registreren?
|
||||
log-connections: true
|
||||
# Moet Sonar nieuwe verificatiepogingen registreren tijdens aanvallen?
|
||||
log-during-attack: false
|
||||
# Moet Sonar elke beweging/positieverandering tijdens de verificatie registreren?
|
||||
# Dit wordt niet aanbevolen voor productieservers, maar kan nuttig zijn bij het opsporen van fouten.
|
||||
debug-xyz-positions: false
|
||||
# Moet Sonar ook Geyser (Bedrock) spelers controleren?
|
||||
# Deze functie is experimenteel en kan problemen veroorzaken
|
||||
# Als dit is uitgeschakeld, worden Bedrock-spelers overgeslagen
|
||||
check-geyser-players: true
|
||||
# De hoeveelheid tijd die moet verstrijken voordat een speler een time-out krijgt
|
||||
# (Deze waarde vertegenwoordigt de tijd in milliseconden: 1 seconde = 1000 milliseconden)
|
||||
read-timeout: 8000
|
||||
# Hoe lang moet een speler wachten voordat hij tijdens de verificatie opnieuw verbinding maakt?
|
||||
# (Deze waarde vertegenwoordigt de tijd in milliseconden: 1 seconde = 1000 milliseconden)
|
||||
rejoin-delay: 8000
|
||||
# Hoe lang moet Sonar het aantal mislukte verificaties voor een speler onthouden?
|
||||
# (Deze waarde vertegenwoordigt de tijd in milliseconden: 1 seconde = 1000 milliseconden)
|
||||
remember-time: 120000
|
||||
# Hoe lang moet een IP-adres worden ontzegd om in te loggen als de verificatie te vaak mislukt?
|
||||
# (Deze waarde vertegenwoordigt de tijd in milliseconden: 1 seconde = 1000 milliseconden)
|
||||
blacklist-time: 600000
|
||||
# Hoe vaak moet een speler de verificatie niet doorstaan om op de zwarte lijst te worden geplaatst?
|
||||
# (Stel deze waarde in op 0 om de zwarte lijst volledig uit te schakelen)
|
||||
blacklist-threshold: 2
|
||||
# Lijst met protocol-ID's die niet door Sonar worden gecontroleerd (verificatie-bypass)
|
||||
# De volledige lijst met alle protocol-ID's vindt u hier:
|
||||
# https://wiki.vg/Protocol_version_numbers
|
||||
# Minecraft 1.20 heeft bijvoorbeeld de ID 763.
|
||||
whitelisted-protocols: []
|
||||
# Lijst met protocol-ID's die helemaal geen verbinding kunnen maken met de server
|
||||
blacklisted-protocols: []
|
||||
@@ -88,12 +88,12 @@ verification:
|
||||
# - ADVENTURE: все компоненты пользовательского интерфейса видны
|
||||
gamemode: ADVENTURE
|
||||
|
||||
# Checks if the client sends proper packets when mounting a vehicle
|
||||
# Проверяет, отправляет ли клиент правильные пакеты при садке на транспортное средство
|
||||
vehicle:
|
||||
# Possible types: ALWAYS, DURING_ATTACK, NEVER
|
||||
# - ALWAYS: New players will always be mounted on a vehicle (Recommended)
|
||||
# - DURING_ATTACK: New players will only be mounted on a vehicle during an attack
|
||||
# - NEVER: New players will never be mounted on a vehicle
|
||||
# Возможные типы: ALWAYS, DURING_ATTACK, NEVER
|
||||
# - ALWAYS: Новые игроки всегда будут садиться на транспортное средство (Рекомендуется)
|
||||
# - DURING_ATTACK: Новые игроки будут садиться на транспортное средство только во время атаки
|
||||
# - NEVER: Новые игроки никогда не будут садиться на транспортное средство
|
||||
timing: ALWAYS
|
||||
|
||||
# Заставить игрока ввести код с картинки в чат
|
||||
@@ -105,38 +105,49 @@ verification:
|
||||
# - NEVER: Новые игроки никогда не получат капчу (Рекомендуется)
|
||||
timing: NEVER
|
||||
# Кастомизация эффектов на картинке, которые показываются игроку
|
||||
# Рекомендуется редактировать только если вы знаете, что делаете
|
||||
effects:
|
||||
# Добавляет свечение к изображению
|
||||
flare: true
|
||||
# Добавляет случайные линии к изображению
|
||||
# Добавляет случайные линии на изображение
|
||||
scratches: true
|
||||
# Применяет искажение ряби (синусоидальное)
|
||||
# Применяет эффект волны (ripple)
|
||||
ripple: true
|
||||
# Применяет горизонтальное искажение
|
||||
smear: true
|
||||
# Добавляет немного размытости/искажения к изображению
|
||||
pinch: true
|
||||
# Применяет цветокоррекцию (насыщенность цвета)
|
||||
saturation: 0.15
|
||||
# Применяет горизонтальное треугольное искажение
|
||||
distortion: 2
|
||||
# Применяет фильтр bump (увеличивает резкость текста)
|
||||
bump: true
|
||||
# Фильтр искажения
|
||||
distortion:
|
||||
# Следует ли применять фильтр искажения?
|
||||
enabled: true
|
||||
# Какую форму следует использовать Sonar для фильтра искажения?
|
||||
# Возможные типы: 0, 1, 2, 3
|
||||
# - 0: Кресты
|
||||
# - 1: Линии
|
||||
# - 2: Круги
|
||||
# - 3: Квадраты
|
||||
shape: 2
|
||||
# Расстояние между искаженными пикселями/объектами
|
||||
distance: 5
|
||||
# Плотность искаженных пикселей/объектов
|
||||
density: 0.1
|
||||
# Степень смешивания между цветами объектов
|
||||
mix: 0.15
|
||||
# Путь к фоновому изображению, которое использует Sonar для CAPTCHA
|
||||
# Если изображение не найдено, будет использован шум как фон по умолчанию
|
||||
background: ''
|
||||
# Следует ли Sonar автоматически определять цвет фона?
|
||||
# Это делается для создания контраста между фоновым изображением и текстом
|
||||
# Просто укажите имя файла здесь, если файл находится в каталоге плагина
|
||||
auto-color: false
|
||||
# Сколько капч должен подготовить Sonar?
|
||||
# Эта задача выполняется асинхронно в фоновом режиме
|
||||
precompute: 500
|
||||
# Как долго Sonar должен ждать, пока игрок не пройдет капча?
|
||||
# (Это значение представляет время в миллисекундах: 1 секунда = 1000 миллисекунд)
|
||||
max-duration: 45000
|
||||
max-duration: 30000
|
||||
# Сколько раз игрок должен завалить капчу, чтобы не пройти проверку?
|
||||
max-tries: 3
|
||||
# Символы (буквы и цифры), которые разрешено использовать в ответе на капчу
|
||||
# It is not recommended to add numbers or letters that might look like one another
|
||||
# Не рекомендуется добавлять цифры или буквы, которые могут быть похожи друг на друга
|
||||
dictionary: '1235689'
|
||||
# Какие типы шрифтов следует использовать в Sonar для текста в капче?
|
||||
font-names:
|
||||
- Dialog
|
||||
- DialogInput
|
||||
- SansSerif
|
||||
- Monospaced
|
||||
|
||||
# Проверяет, отправляет ли игрок на сервер действительный клиентский бренд.
|
||||
client-brand:
|
||||
@@ -157,22 +168,22 @@ verification:
|
||||
# Максимальное количество пакетов для входа на сервер, которое игрок должен отправить, чтобы быть выгнанным
|
||||
max-login-packets: 256
|
||||
|
||||
# If enabled, the player will be transferred back to the origin server
|
||||
# after successfully passing the bot verification.
|
||||
# This feature was introduced by Mojang in Minecraft version 1.20.5
|
||||
# Если включено, игрок будет возвращен на исходный сервер
|
||||
# после успешного прохождения проверки на бота.
|
||||
# Эта функция была введена Mojang в версии Minecraft 1.20.5
|
||||
transfer:
|
||||
# Should Sonar transfer the player to the origin server (instead of kicking them)?
|
||||
# For this to work, you must enable the feature in your server's configuration
|
||||
# Additionally, you might want to reduce the amount of login rate-limiting
|
||||
# performed by Velocity or other proxies/plugins, as this might prevent the
|
||||
# player from being transferred correctly.
|
||||
# Должен ли Sonar переместить игрока на исходный сервер (вместо выкидывания его)?
|
||||
# Для этого необходимо включить функцию в конфигурации вашего сервера
|
||||
# Кроме того, вы можете пожелать снизить количество ограничений на скорость входа
|
||||
# выполняемых Velocity или другими прокси/плагинами, так как это может помешать
|
||||
# корректному перемещению игрока.
|
||||
enabled: false
|
||||
# Which server should Sonar transfer the player to when the verification is passed?
|
||||
# Please enter the server IP that is used by players to normally connect to your server.
|
||||
# For example, you can put "mc.hypixel.net" or a direct IP like "1.1.1.1"
|
||||
# На какой сервер должен Sonar переместить игрока после прохождения проверки?
|
||||
# Введите IP-адрес сервера, который обычно используется игроками для подключения к вашему серверу.
|
||||
# Например, вы можете указать "mc.hypixel.net" или прямой IP-адрес, например "1.1.1.1"
|
||||
destination-host: "play.my-server.com"
|
||||
# Which port should Sonar use when transferring the player to the origin server?
|
||||
# If your server does not need a port to connect to, you can leave this as 25565.
|
||||
# Какой порт должен использовать Sonar при перемещении игрока на исходный сервер?
|
||||
# Если ваш сервер не требует порта для подключения, вы можете оставить это как 25565.
|
||||
destination-port: 25565
|
||||
|
||||
# Должен ли Sonar регистрировать новые попытки проверки?
|
||||
@@ -182,9 +193,9 @@ verification:
|
||||
# Должен ли Sonar регистрировать каждое движение/изменение положения во время проверки?
|
||||
# Это не рекомендуется для рабочих серверов, но может быть полезно для выявления ошибок.
|
||||
debug-xyz-positions: false
|
||||
# Should Sonar also check Geyser (Bedrock) players?
|
||||
# This feature is experimental and might cause issues
|
||||
# If this is disabled, Bedrock players will be skipped
|
||||
# Следует ли Sonar также проверять игроков Geyser (Bedrock)?
|
||||
# Эта функция экспериментальна и может вызывать проблемы
|
||||
# Если это отключено, игроки Bedrock будут пропущены
|
||||
check-geyser-players: true
|
||||
# Количество времени, которое должно пройти, прежде чем игрока кикнет.
|
||||
# (Это значение представляет время в миллисекундах: 1 секунда = 1000 миллисекунд)
|
||||
|
||||
@@ -103,36 +103,47 @@ verification:
|
||||
# - NEVER: 永远不应用该检查
|
||||
timing: NEVER
|
||||
# 图像效果选项 (用于混淆自动图像识别)
|
||||
# You should only edit this if you know what you're doing
|
||||
effects:
|
||||
# 是否添加局部发光效果
|
||||
flare: true
|
||||
# 是否添加随机的线条
|
||||
# Adds random lines to the image
|
||||
scratches: true
|
||||
# 是否应用波纹失真效果
|
||||
# Applies a ripple filter (wavy effect)
|
||||
ripple: true
|
||||
# 是否应用纵向畸变滤镜
|
||||
smear: true
|
||||
# 是否添加一些失真
|
||||
pinch: true
|
||||
# 图像的色彩饱和度
|
||||
saturation: 0.15
|
||||
# 水平三角形纹波滤镜
|
||||
distortion: 2
|
||||
# Applies a bump filter (sharpened text)
|
||||
bump: true
|
||||
# Distortion filter
|
||||
distortion:
|
||||
# Should Sonar apply a distortion filter?
|
||||
enabled: true
|
||||
# What shape should Sonar use for the distortion filter?
|
||||
# Possible types: 0, 1, 2, 3
|
||||
# - 0: Crosses
|
||||
# - 1: Lines
|
||||
# - 2: Circles
|
||||
# - 3: Squares
|
||||
shape: 2
|
||||
# Distance between distorted pixels/objects
|
||||
distance: 5
|
||||
# Density of the distorted pixels/objects
|
||||
density: 0.1
|
||||
# Amount of blending between the colors of objects
|
||||
mix: 0.15
|
||||
# Path to the background image Sonar uses for the CAPTCHA
|
||||
# If the image isn't found, a noise will be used as the default background
|
||||
background: ''
|
||||
# Should Sonar automatically detect the background color?
|
||||
# This is done to create a contrast between the background image and the text
|
||||
# You can simply put the name of the file here if file is in the plugin directory
|
||||
auto-color: false
|
||||
# Sonar 应该一次准备多少个验证码?
|
||||
precompute: 500
|
||||
# Sonar 应该给玩家多少时间完成验证码? (该值为毫秒. 1秒等于1000毫秒.)
|
||||
max-duration: 45000
|
||||
max-duration: 30000
|
||||
# 玩家最多能输入多少次错误的验证码?
|
||||
max-tries: 3
|
||||
# 允许出现在验证码答案中的字符
|
||||
# 不建议添加可能看起来彼此相似的数字或字母
|
||||
dictionary: '1235689'
|
||||
# Sonar 应该为验证码使用哪些字体?
|
||||
font-names:
|
||||
- Dialog
|
||||
- DialogInput
|
||||
- SansSerif
|
||||
- Monospaced
|
||||
|
||||
# 检查玩家是否正在向服务器发送有效的客户端标识符
|
||||
client-brand:
|
||||
|
||||
@@ -184,9 +184,6 @@ verification:
|
||||
captcha:
|
||||
# Nachricht, die dem Spieler angezeigt wird, wenn er den CAPTCHA-Code im Chat eingeben muss
|
||||
enter-code: '%prefix%<green>Bitte gib den Code im Chat ein, der auf der Karte angezeigt wird.'
|
||||
# Timer, der dem Spieler angezeigt wird, wenn er den CAPTCHA-Code im Chat eingeben muss
|
||||
# (Setze dies auf '' um die Aktionsleisten-Nachricht zu deaktivieren)
|
||||
action-bar: '%prefix%<green>Du hast %time-left% Sekunden Zeit, um den Code im Chat einzugeben'
|
||||
# Nachricht, die dem Spieler angezeigt wird, wenn er den falschen Code im Chat eingibt
|
||||
incorrect: '%prefix%<red>Du hast den falschen Code eingegeben. Bitte versuche es erneut.'
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ commands:
|
||||
|
||||
# Translations for '/sonar blacklist'
|
||||
blacklist:
|
||||
# Message that is shown when someone tries clearing the blacklist but is is empty
|
||||
# Message that is shown when someone tries clearing the blacklist but is empty
|
||||
empty: '%prefix%The blacklist is currently empty. Therefore, no IP addresses were
|
||||
removed from the blacklist.'
|
||||
# Message that is shown when someone clears the blacklist
|
||||
@@ -188,10 +188,6 @@ verification:
|
||||
# Message that is shown to the player when they have to enter the answer to the captcha
|
||||
enter-code: '%prefix%<green>Please enter the code in chat that is displayed on
|
||||
the map.'
|
||||
# Timer that is shown to the player when they have to enter the answer to the captcha
|
||||
# (Set this to '' to disable the action bar message)
|
||||
action-bar: '%prefix%<green>You have %time-left% seconds left to enter the code
|
||||
in chat'
|
||||
# Message that is shown to the player when they enter the wrong answer in chat
|
||||
incorrect: '%prefix%<red>You have entered the wrong code. Please try again.'
|
||||
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
#
|
||||
# /$$$$$$
|
||||
# /$$__ $$
|
||||
# | $$ \__/ /$$$$$$ /$$$$$$$ /$$$$$$ /$$$$$$
|
||||
# | $$$$$$ /$$__ $$| $$__ $$ |____ $$ /$$__ $$
|
||||
# \____ $$| $$ \ $$| $$ \ $$ /$$$$$$$| $$ \__/
|
||||
# /$$ \ $$| $$ | $$| $$ | $$ /$$__ $$| $$
|
||||
# | $$$$$$/| $$$$$$/| $$ | $$| $$$$$$$| $$
|
||||
# \______/ \______/ |__/ |__/ \_______/|__/
|
||||
#
|
||||
# Hulp nodig of vragen? https://jonesdev.xyz/discord
|
||||
# Broncode: https://github.com/jonesdevelopment/sonar
|
||||
# Copyright Jones Development and Sonar Contributors, licensed under GPL-3.0
|
||||
#
|
||||
|
||||
# Tijdelijke aanduiding voor elke '%prefix%' in dit configuratiebestand
|
||||
prefix: <b><yellow>Sonar</b> <gray>» <white>
|
||||
# Tijdelijke aanduiding voor elke '%support-url%' in dit configuratiebestand
|
||||
support-url: https://jonesdev.xyz/discord/
|
||||
# Tijdelijke aanduiding voor elke '%header%' in dit configuratiebestand
|
||||
header:
|
||||
- <b><yellow>Sonar</b><reset>
|
||||
- ''
|
||||
# Tijdelijke aanduiding voor elke '%footer%' in dit configuratiebestand
|
||||
footer:
|
||||
- <gray>Als u denkt dat dit een fout is, neem dan contact op met een beheerder.
|
||||
# Verbindingsbericht dat wordt weergegeven als iemand zich aansluit, maar er zijn te veel online spelers met hun IP-adres
|
||||
too-many-online-per-ip:
|
||||
- '%header%'
|
||||
- <red>Er zijn te veel spelers online met uw IP-adres.
|
||||
- '%footer%'
|
||||
|
||||
# Vertalingen voor commandos
|
||||
commands:
|
||||
# Bericht dat wordt weergegeven wanneer een speler zonder toestemming /sonar probeert uit te voeren
|
||||
no-permission: '%prefix%<red>U heeft geen toestemming om deze opdracht uit te voeren.'
|
||||
# Bericht dat verschijnt wanneer iemand een commando verkeerd gebruikt
|
||||
incorrect-usage: '%prefix%<red>Gebruik: /sonar %usage%'
|
||||
# Bericht dat wordt weergegeven wanneer iemand een ongeldig IP-adres opgeeft (ongeldig formaat)
|
||||
invalid-ip-address: '%prefix%Het door u opgegeven IP-adres lijkt ongeldig te zijn.'
|
||||
# Bericht dat wordt weergegeven wanneer de console een opdracht uitvoert die alleen voor spelers is
|
||||
player-only: '%prefix%<red>Je kunt dit commando alleen als speler uitvoeren.'
|
||||
# Bericht dat wordt weergegeven wanneer een speler een opdracht uitvoert die alleen voor de console geldt
|
||||
console-only: '%prefix%<red>Om veiligheidsredenen kunt u deze opdracht alleen uitvoeren
|
||||
via console.'
|
||||
# Bericht dat wordt weergegeven wanneer een speler Sonar-opdrachten te snel uitvoert
|
||||
cool-down: '%prefix%<red>U kunt dit commando slechts elke 0,5 seconde uitvoeren.'
|
||||
cool-down-left: '%prefix%<red>Wacht nog <bold>%time-left%s<reset><red>.'
|
||||
# Bericht dat wordt getoond wanneer een speler geen toestemming heeft om een bepaald subcommand uit te voeren
|
||||
subcommand-no-permission: '%prefix%<red>U heeft geen toestemming om dit uit te voeren
|
||||
subcommand. <gray>(%permission%)'
|
||||
|
||||
# Vertalingen voor '/sonar'
|
||||
main:
|
||||
# Informatief bericht dat boven alles wordt weergegeven bij het uitvoeren van de hoofdopdracht
|
||||
header:
|
||||
- <yellow><b>Sonar</b> %version% draaien op %platform%.
|
||||
- <gray><click:open_url:'https://github.com/jonesdevelopment/sonar'>(C) %copyright_year%
|
||||
Jones Development and Sonar Contributors</click>
|
||||
- ''
|
||||
- <gray>Hulp nodig of vragen?
|
||||
- <white><click:open_url:'https://jonesdev.xyz/discord/'><hover:show_text:'(Klik
|
||||
om Discord te openen)'>Open een ticket op de Discord </hover></click><click:open_url:'https://github.com/jonesdevelopment/sonar/issues'><hover:show_text:'(Klik
|
||||
om GitHub te openen)'>of open een nieuw probleem op GitHub.
|
||||
- ''
|
||||
# Opmaak van de lijst met subcommand die wordt weergegeven bij het uitvoeren van de hoofdopdracht
|
||||
subcommands: '<click:suggest_command:''/sonar %subcommand% ''><hover:show_text:''<gray>Alleen
|
||||
spelers: </gray>%only_players%<br><gray>Vereist console: </gray>%require_console%<br><gray>Toestemming:
|
||||
</gray><white>%permission%<br><gray>Aliassen: </gray>%aliases%''><dark_aqua>
|
||||
▪ <gray>/sonar %subcommand% <white>%description%'
|
||||
|
||||
# Vertalingen voor '/sonar reload'
|
||||
reload:
|
||||
# Bericht dat wordt weergegeven wanneer iemand Sonar begint te herladen
|
||||
start: '%prefix%Sonar opnieuw laden...'
|
||||
# Bericht dat wordt weergegeven wanneer Sonar klaar is met herladen
|
||||
finish: '%prefix%<green>Opnieuw geladen <gray>(%taken%ms)'
|
||||
|
||||
# Vertalingen voor '/sonar verbose'
|
||||
verbose:
|
||||
# Bericht dat wordt weergegeven wanneer een speler zich abonneert op Sonar uitgebreid
|
||||
subscribed: '%prefix%U bekijkt nu Sonar uitgebreid.'
|
||||
# Bericht dat wordt weergegeven wanneer een speler zich afmeldt voor Sonar uitgebreid
|
||||
unsubscribed: '%prefix%Je bekijkt Sonar niet langer uitgebreid.'
|
||||
|
||||
# Vertalingen voor '/sonar notify'
|
||||
notify:
|
||||
# Bericht dat wordt weergegeven wanneer een speler zich abonneert op Sonar aanval meldingen
|
||||
subscribed: '%prefix%U bekijkt nu Sonar aanval meldingen.'
|
||||
# Bericht dat wordt weergegeven wanneer een speler zich afmeldt voor Sonar aanval meldingen
|
||||
unsubscribed: '%prefix%Je bekijkt Sonar niet langer Sonar aanval meldingen.'
|
||||
|
||||
# Vertalingen voor '/sonar blacklist'
|
||||
blacklist:
|
||||
# Bericht dat wordt weergegeven wanneer iemand de zwarte lijst probeert te wissen, maar deze leeg is
|
||||
empty: '%prefix%De zwarte lijst is momenteel leeg. Daarom waren er geen IP-adressen
|
||||
verwijderd van de zwarte lijst.'
|
||||
# Bericht dat wordt weergegeven wanneer iemand de zwarte lijst wist
|
||||
cleared: '%prefix%Je hebt in totaal %removed% IP-adres(sen) met succes verwijderd
|
||||
van de zwarte lijst.'
|
||||
# Bericht dat wordt getoond wanneer iemand de omvang van de zwarte lijst controleert
|
||||
size: '%prefix%De zwarte lijst bevat momenteel %amount% IP-adres(sen).'
|
||||
# Bericht dat wordt getoond wanneer iemand een IP-adres aan de zwarte lijst toevoegt
|
||||
added: '%prefix%%ip% is succesvol toegevoegd aan de zwarte lijst.'
|
||||
# Bericht dat wordt getoond wanneer iemand een IP-adres toevoegt aan de zwarte lijst die is geverifieerd
|
||||
added-warning: '%prefix%<red>Attentie: <white>%ip% staat momenteel op de witte lijst. Overwegen
|
||||
het verwijderen van het IP-adres uit de lijst met geverifieerde spelers om potentieel te voorkomen
|
||||
problemen.'
|
||||
# Bericht dat verschijnt wanneer iemand een IP-adres van de zwarte lijst verwijdert
|
||||
removed: '%prefix%%ip% is met succes verwijderd van de zwarte lijst.'
|
||||
# Bericht dat wordt weergegeven wanneer iemand een IP-adres aan de zwarte lijst toevoegt, maar dit al op de zwarte lijst staat
|
||||
duplicate-ip: '%prefix%Het door u opgegeven IP-adres staat al op de zwarte lijst.'
|
||||
# Bericht dat verschijnt wanneer iemand een IP-adres van de zwarte lijst verwijdert, maar dit niet op de zwarte lijst staat
|
||||
ip-not-found: '%prefix%Het door u opgegeven IP-adres staat niet op de zwarte lijst.'
|
||||
|
||||
# Vertalingen voor '/sonar verified'
|
||||
verified:
|
||||
# Bericht dat wordt weergegeven wanneer iemand de lijst met geverifieerde spelers probeert te wissen, maar leeg is
|
||||
empty: '%prefix%De lijst met geverifieerde spelers is momenteel leeg. Daarom nee
|
||||
spelers waren niet geverifieerd.'
|
||||
# Bericht dat wordt weergegeven wanneer iemand de lijst met geverifieerde spelers wist
|
||||
cleared: '%prefix%Je hebt met succes de verificatie van in totaal %removed% unieke speler(s) ongedaan gemaakt.'
|
||||
# Bericht dat wordt weergegeven wanneer iemand de grootte van de lijst met geverifieerde spelers controleert
|
||||
size: '%prefix%Er zijn momenteel %amount% unieke speler(s) geverifieerd.'
|
||||
# Bericht dat wordt weergegeven wanneer iemand de verificatie van een IP-adres ongedaan maakt
|
||||
removed: '%prefix%De verificatie van %ip% is succesvol ingetrokken.'
|
||||
# Bericht dat wordt weergegeven wanneer iemand een IP-adres verifieert
|
||||
added: '%prefix%%ip% succesvol geverifieerd.'
|
||||
# Bericht dat wordt weergegeven wanneer iemand een IP-adres/UUID probeert te verifiëren die al is geverifieerd
|
||||
already: '%prefix%Het IP-adres en de UUID die u heeft opgegeven, zijn al geverifieerd.'
|
||||
# Bericht dat wordt weergegeven wanneer iemand de verificatie van een IP-adres ongedaan maakt, maar het is niet geverifieerd
|
||||
ip-not-found: '%prefix%Het door u opgegeven IP-adres is niet geverifieerd.'
|
||||
# Bericht dat wordt weergegeven wanneer iemand tweemaal hetzelfde IP-adres probeert te de-verifiëren (dubbele handeling)
|
||||
blocked: '%prefix%Wacht tot de huidige bewerking is voltooid.'
|
||||
|
||||
# Vertalingen voor '/sonar statistics'
|
||||
statistics:
|
||||
# Informatieve boodschap die boven alles wordt getoond bij het bekijken van de statistieken
|
||||
header: '%prefix%Er worden %type% statistieken voor deze sessie weergegeven:'
|
||||
# Bericht dat wordt weergegeven wanneer een speler een onbekende statistiek probeert te bekijken
|
||||
unknown-type: '%prefix%<red>Onbekend statistieke! Beschikbare statistieken: <gray>%statistics%'
|
||||
# Formaat van het algemene statistische bericht
|
||||
general:
|
||||
- ' <dark_aqua>▪ <gray>Geverifieerde IP-adressen: <white>%verified%'
|
||||
- ' <dark_aqua>▪ <gray>IP-adressen verifiëren: <white>%verifying%'
|
||||
- ' <dark_aqua>▪ <gray>IP-adressen op de zwarte lijst: <white>%blacklisted%'
|
||||
- ' <dark_aqua>▪ <gray>Wachtrij staande aanmeldingen: <white>%queued%'
|
||||
- ' <dark_aqua>▪ <gray>Totaal aantal niet-unieke joins: <white>%total_joins%'
|
||||
- ' <dark_aqua>▪ <gray>Totaal aantal verificatiepogingen: <white>%total_attempts%'
|
||||
- ' <dark_aqua>▪ <gray>Totaal aantal mislukte verificaties: <white>%total_failed%'
|
||||
- ' <dark_aqua>▪ <gray>Server-uptime: <white>%uptime%'
|
||||
# Formaat van het bericht over CPU-statistieken
|
||||
cpu:
|
||||
- ' <dark_aqua>▪ <gray>Verwerk het CPU-gebruik nu: <white>%process_cpu%%'
|
||||
- ' <dark_aqua>▪ <gray>Systeem-CPU-gebruik nu: <white>%system_cpu%%'
|
||||
- ' <dark_aqua>▪ <gray>CPU-gebruik per kernproces: <white>%average_process_cpu%%'
|
||||
- ' <dark_aqua>▪ <gray>CPU-gebruik per core systeem: <white>%average_system_cpu%%'
|
||||
- ' <dark_aqua>▪ <gray>Algemene systeembelasting: <white>%load_average%%'
|
||||
- ' <dark_aqua>▪ <gray>Totaal aantal virtuele CPUs: <white>%virtual_cores%'
|
||||
# Formaat van het bericht met geheugenstatistieken
|
||||
memory:
|
||||
- ' <dark_aqua>▪ <gray>Totaal vrij geheugen: <white>%free_memory%'
|
||||
- ' <dark_aqua>▪ <gray>Totaal gebruikt geheugen: <white>%used_memory%'
|
||||
- ' <dark_aqua>▪ <gray>Totaal maximaal geheugen: <white>%max_memory%'
|
||||
- ' <dark_aqua>▪ <gray>Totaal toegewezen geheugen: <white>%total_memory%'
|
||||
# Formaat van het bericht met netwerkstatistieken
|
||||
network:
|
||||
- ' <dark_aqua>▪ <gray>Huidige inkomende gebruikte bandbreedte: <white>%incoming%'
|
||||
- ' <dark_aqua>▪ <gray>Huidige uitgaande gebruikte bandbreedte: <white>%outgoing%'
|
||||
- ' <dark_aqua>▪ <gray>Totale inkomende gebruikte bandbreedte: <white>%ttl_incoming%'
|
||||
- ' <dark_aqua>▪ <gray>Totale uitgaande gebruikte bandbreedte: <white>%ttl_outgoing%'
|
||||
|
||||
# Vertalingen voor alle berichten tijdens het verificatieproces
|
||||
verification:
|
||||
# Vertalingen voor alle debug-berichten tijdens de verificatie
|
||||
logs:
|
||||
# Bericht dat in de console wordt geregistreerd wanneer een nieuwe speler zich bij de server voegt
|
||||
connection: '%name%%ip% (%protocol%) heeft verbinding gemaakt.'
|
||||
# Bericht dat in de console wordt geregistreerd wanneer de verificatie van een speler mislukt
|
||||
failed: '%ip% (%protocol%) heeft de bot-controle niet doorstaan vanwege: %reason%'
|
||||
# Bericht dat op de console wordt geregistreerd wanneer een speler op de zwarte lijst staat
|
||||
blacklisted: '%ip% (%protocol%) stond op de zwarte lijst vanwege te veel mislukte pogingen'
|
||||
# Bericht dat op de console wordt geregistreerd wanneer een speler is geverifieerd
|
||||
successful: '%name% is succesvol geverifieerd (%time%s!).'
|
||||
|
||||
# Berichten die aan de speler worden getoond wanneer hij of zij de CAPTCHA moet invoeren
|
||||
captcha:
|
||||
# Bericht dat aan de speler wordt getoond wanneer hij het antwoord op de captcha moet invoeren
|
||||
enter-code: '%prefix%<green>Voer de code in de chat in die wordt weergegeven op
|
||||
de kaart.'
|
||||
# Bericht dat aan de speler wordt getoond wanneer hij of zij het verkeerde antwoord invoert in de chat
|
||||
incorrect: '%prefix%<red>U heeft de verkeerde code ingevoerd. Probeer het opnieuw.'
|
||||
|
||||
# Verbindingsbericht dat wordt getoond als iemand zich aansluit terwijl de captcha nog niet is voorbereid
|
||||
currently-preparing:
|
||||
- '%header%'
|
||||
- <yellow>Uw anti-bot gegevens zijn nog niet voorbereid.
|
||||
- <gray>Wacht een paar seconden voordat u opnieuw probeert te verifiëren.
|
||||
- '%footer%'
|
||||
# Verbindingsbericht dat wordt weergegeven wanneer iemand tijdens de verificatie te snel opnieuw deelneemt
|
||||
too-fast-reconnect:
|
||||
- '%header%'
|
||||
- <gold>Je hebt te snel opnieuw verbinding gemaakt. Probeer het later opnieuw.
|
||||
- <gray>Wacht een paar seconden voordat u opnieuw probeert te verifiëren.
|
||||
- '%footer%'
|
||||
# Verbindingsbericht dat wordt weergegeven wanneer iemand zich aanmeldt, maar zich al aan het verifiëren is
|
||||
already-verifying:
|
||||
- '%header%'
|
||||
- <red>Uw IP-adres wordt momenteel geverifieerd.
|
||||
- <red>Wacht een paar seconden voordat u opnieuw probeert te verifiëren.
|
||||
- '%footer%'
|
||||
# Verbindingsbericht dat wordt weergegeven wanneer iemand zich aanmeldt, maar al in de wachtrij staat voor verificatie
|
||||
already-queued:
|
||||
- '%header%'
|
||||
- <red>Uw IP-adres staat momenteel in de wachtrij voor verificatie.
|
||||
- <red>Wacht een paar minuten voordat u opnieuw probeert te verifiëren.
|
||||
- '%footer%'
|
||||
# Verbindingsbericht dat wordt weergegeven wanneer iemand lid wordt, maar tijdelijk op de zwarte lijst staat
|
||||
blacklisted:
|
||||
- '%header%'
|
||||
- <red>Momenteel wordt u de toegang tot de server ontzegd.
|
||||
- <red>Wacht een paar minuten voordat u weer verbinding kunt maken met de server.
|
||||
- <gold>Vals positief? <gray>%support-url%
|
||||
- '%footer%'
|
||||
# Verbindingsbericht dat wordt weergegeven wanneer iemand zich aanmeldt met een ongeldige gebruikersnaam
|
||||
invalid-username:
|
||||
- '%header%'
|
||||
- <red>Uw gebruikersnaam bevat ongeldige tekens.
|
||||
- '%footer%'
|
||||
# Verbindingsbericht dat wordt weergegeven wanneer iemand zich aanmeldt met een versie op de zwarte lijst
|
||||
blacklisted-protocol:
|
||||
- '%header%'
|
||||
- <red>U gebruikt een versie die niet is toegestaan op onze server.
|
||||
- <gold>Hulp nodig bij het inloggen? <gray>%support-url%
|
||||
- '%footer%'
|
||||
# Verbindingsbericht dat wordt weergegeven wanneer iemand succesvol verifieert
|
||||
success:
|
||||
- '%header%'
|
||||
- <green>U heeft de verificatie succesvol doorlopen.
|
||||
- <white>Wanneer u opnieuw verbinding maakt, kunt u nu op de server spelen.
|
||||
# Verbindingsbericht dat wordt weergegeven wanneer iemand de verificatie mislukt
|
||||
failed:
|
||||
- '%header%'
|
||||
- <red>De bot-verificatie is mislukt.
|
||||
- <gray>Wacht een paar seconden voordat u opnieuw probeert te verifiëren.
|
||||
- <gold>Hulp nodig? <gray>%support-url%
|
||||
- '%footer%'
|
||||
|
||||
# Vertalingen voor Sonar's actiebalk uitgebreide berichten
|
||||
verbose:
|
||||
layout:
|
||||
# Lay-out voor de actiebalk van Sonar, uitgebreid
|
||||
# Tijdelijke aanduidingen:
|
||||
# - '%queued%' Aantal verbindingen in de wachtrij
|
||||
# - '%verifying%' Aantal verifiërende verbindingen
|
||||
# - '%blacklisted%' Aantal op de zwarte lijst geplaatste IP-adressen
|
||||
# - '%total-joins%' Aantal totale pogingen tot deelname
|
||||
# - '%verify-total%' Aantal totale verificatiepogingen
|
||||
# - '%verify-success%' Aantal geverifieerde IP-adressen
|
||||
# - '%verify-failed%' Aantal mislukte verificaties
|
||||
# - '%logins-per-second%' Aantal logins per seconde
|
||||
# - '%connections-per-second%' Aantal verbindingen per seconde
|
||||
# - '%attack-duration%' Duur van de huidige aanval
|
||||
# - '%incoming-traffic%' Inkomend bandbreedtegebruik per seconde
|
||||
# - '%outgoing-traffic%' Uitgaand bandbreedtegebruik per seconde
|
||||
# - '%incoming-traffic-ttl%' Totaal inkomend bandbreedtegebruik
|
||||
# - '%outgoing-traffic-ttl%' Totaal uitgaand bandbreedtegebruik
|
||||
# - '%used-memory%' Hoeveelheid gebruikt geheugen van het proces
|
||||
# - '%total-memory%' Hoeveelheid totale geheugen van het proces
|
||||
# - '%max-memory%' Hoeveelheid maximaal geheugen van het proces
|
||||
# - '%free-memory%' Hoeveelheid vrij geheugen van het proces
|
||||
# - '%animation%' Aanpasbaar geanimeerd symbool
|
||||
# Vertalingen voor de normale uitgebreide uitvoer van Sonar
|
||||
normal: '%prefix%<gray>CPS <white>%connections-per-second% <dark_aqua>╺ <gray>Inloggen/s
|
||||
<white>%logins-per-second% <dark_aqua>╺ <gray>Verifiëren <white>%verifying% <dark_aqua>|
|
||||
<green>⬆ <white>%outgoing-traffic%/s <red>⬇ <white>%incoming-traffic%/s <green><bold>%animation%<reset>'
|
||||
# Layout for Sonar's actionbar verbose during an attack
|
||||
attack: '%prefix%<gray>CPS <white>%connections-per-second% <dark_aqua>╺ <gray>Inloggen/s
|
||||
<white>%logins-per-second% <dark_aqua>╺ <gray>Wachtrij <white>%queued% <dark_aqua>╺
|
||||
<gray>Verifiëren <white>%verifying% <dark_aqua>╺ <gray>Duur <white>%attack-duration%
|
||||
<dark_aqua>| <green>⬆ <white>%outgoing-traffic%/s <red>⬇ <white>%incoming-traffic%/s <green><bold>%animation%<reset>'
|
||||
# Animatie voor de actiebalk
|
||||
# Alternatieven:
|
||||
# - ▙, ▛, ▜, ▟
|
||||
# - ⬈, ⬊, ⬋, ⬉
|
||||
animation:
|
||||
- ◜
|
||||
- ◝
|
||||
- ◞
|
||||
- ◟
|
||||
|
||||
# Vertalingen voor Sonar's aanval meldingen
|
||||
notifications:
|
||||
# Lay-out voor de titel van de aanval meldingen van Sonar
|
||||
title: <yellow><b>Sonar<reset>
|
||||
# Lay-out voor de ondertitel van Sonar's aanval meldingen
|
||||
subtitle: <gray>A bot attack has been detected
|
||||
# Lay-out voor de aanval meldingen van Sonar in de chat
|
||||
chat:
|
||||
- ''
|
||||
- <yellow><b>Sonar<reset>
|
||||
- ''
|
||||
- <gray>Er is een bot-aanval gedetecteerd.
|
||||
- ' <dark_aqua>▪ <gray>Inloggen per seconde: <white>%logins-per-second%'
|
||||
- ' <dark_aqua>▪ <gray>Spelers verifiëren: <white>%verifying%'
|
||||
- ' <dark_aqua>▪ <gray>Spelers in de wachtrij: <white>%queued%'
|
||||
- ''
|
||||
- <gray>Bekijk meer informatie via <white><click:run_command:'/sonar verbose'><hover:show_text:'(Klik
|
||||
om uit te voeren)'>/sonar verbose</hover></click><gray> of <white><click:run_command:'/sonar
|
||||
stats'><hover:show_text:'(Klik om uit te voeren)'>/sonar stats</hover></click><gray>.
|
||||
- ''
|
||||
@@ -185,10 +185,6 @@ verification:
|
||||
captcha:
|
||||
# Сообщение, которое показывается игроку, когда он должен ввести ответ на капчу
|
||||
enter-code: '%prefix%<green>Введите код в чате, который отображается на карте.'
|
||||
# Таймер, который показывается игроку, когда он должен ввести капчу
|
||||
# (Установите это значение на '' что-бы отключить эту функцию)
|
||||
action-bar: '%prefix%<green>У вас осталось %time-left% секунд, чтобы ввести код
|
||||
в чате'
|
||||
# Сообщение, которое показывается игроку, когда он вводит неправильную капчу в чат
|
||||
incorrect: '%prefix%<red>Вы ввели неправильный код. Пожалуйста, попробуйте еще раз.'
|
||||
|
||||
|
||||
@@ -176,9 +176,6 @@ verification:
|
||||
captcha:
|
||||
# Captcha'nın cevabını girmeleri gerektiğinde oyuncuya gösterilen mesaj
|
||||
enter-code: '%prefix%<green>Haritada gösterilen kodu lütfen sohbete girin.'
|
||||
# Timer that is shown to the player when they have to enter the answer to the captcha
|
||||
# (Set this to '' to disable the action bar message)
|
||||
action-bar: '%prefix%<green>Kodu girmek için %time-left% saniyeniz kaldı'
|
||||
# Message that is shown to the player when they enter the wrong answer in chat
|
||||
incorrect: '%prefix%<red>Yanlış kodu girdiniz. Lütfen tekrar deneyin.'
|
||||
|
||||
|
||||
@@ -180,8 +180,6 @@ verification:
|
||||
captcha:
|
||||
# 玩家需要输入验证码答案时显示的消息
|
||||
enter-code: '%prefix%<green>请在聊天框中发送地图上的验证码.'
|
||||
# 显示还有多少秒来解决验证码. (设置为空即禁用)
|
||||
action-bar: '%prefix%<green>您还有 %time-left% 秒来解决该验证码.'
|
||||
# 当玩家输入错误的验证码时显示的消息
|
||||
incorrect: '%prefix%<red>您输入了错误的验证码. 请再试一次'
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#
|
||||
# /$$$$$$
|
||||
# /$$__ $$
|
||||
# | $$ \__/ /$$$$$$ /$$$$$$$ /$$$$$$ /$$$$$$
|
||||
# | $$$$$$ /$$__ $$| $$__ $$ |____ $$ /$$__ $$
|
||||
# \____ $$| $$ \ $$| $$ \ $$ /$$$$$$$| $$ \__/
|
||||
# /$$ \ $$| $$ | $$| $$ | $$ /$$__ $$| $$
|
||||
# | $$$$$$/| $$$$$$/| $$ | $$| $$$$$$$| $$
|
||||
# \______/ \______/ |__/ |__/ \_______/|__/
|
||||
#
|
||||
# Hulp nodig of vragen? https://jonesdev.xyz/discord
|
||||
# Broncode: https://github.com/jonesdevelopment/sonar
|
||||
# Copyright Jones Development and Sonar Contributors, licensed under GPL-3.0
|
||||
#
|
||||
|
||||
# Bot aanval meldingen kunnen ook met behulp van webhooks naar uw Discord-server worden verzonden
|
||||
webhook:
|
||||
# URL van de Discord-webhook (zet dit op '' om webhooks uit te schakelen)
|
||||
url: ''
|
||||
# Gebruikersnaam van de Discord-webhook-afzender
|
||||
username: Sonar
|
||||
# URL naar de avatar van de Discord-webhook-afzender (zet dit op '' om uit te schakelen)
|
||||
avatar-url: ''
|
||||
# Inhoud van het Discord-webhook (zet dit op '' om uit te schakelen)
|
||||
# U kunt dit gebruiken om bv. ping medewerkers met <@userId>
|
||||
# Als u rollen wilt pingen, moet u <@&roleId> gebruiken
|
||||
content: ''
|
||||
# Embed Discord-webhook dat wordt verzonden wanneer een aanval is gestopt
|
||||
embed:
|
||||
# Klein voettekst bericht van de Discord-webhook-insluiting
|
||||
footer:
|
||||
# Inhoud van de voettekst bericht van de Discord-webhook-insluiting
|
||||
text: © Jones Development and Sonar Contributors
|
||||
# URL van de voettekst pictogram van de Discord-webhook-insluiting
|
||||
icon-url: ''
|
||||
# Titel van de Discord-webhook-insluiting
|
||||
title: ':white_check_mark: Aanval verzacht'
|
||||
# Klikbare URL van de titel van de Discord-webhook-insluiting
|
||||
title-url: ''
|
||||
# Beschrijving (inhoud) van de Discord-webhook-insluiting
|
||||
description:
|
||||
- De aanval op uw server is afgezwakt.
|
||||
- ''
|
||||
- 'Aanval beginnen: <t:%start-timestamp%:T>'
|
||||
- 'Einde aanval: <t:%end-timestamp%:T>'
|
||||
- 'Duur van de aanval: %duration%'
|
||||
- ''
|
||||
- 'Maximaal proces CPU-gebruik tijdens de aanval: %peak-cpu%%'
|
||||
- 'Maximaal proces-geheugengebruik tijdens de aanval: %peak-memory%'
|
||||
- 'Maximaal bots per seconde tijdens de aanval: %peak-bps%'
|
||||
- ''
|
||||
- 'IP-adressen op de zwarte lijst gezet tijdens de aanval: %total-blacklisted%'
|
||||
- 'Mislukte verificaties tijdens de aanval: %total-failed%'
|
||||
- 'Succesvolle verificaties tijdens de aanval: %total-success%'
|
||||
# RGB-kleuren van de Discord-webhook-insluiting
|
||||
# Kleur kiezer: https://www.rapidtables.com/web/color/RGB_Color.html
|
||||
color:
|
||||
red: 0
|
||||
green: 255
|
||||
blue: 0
|
||||
@@ -24,6 +24,18 @@
|
||||
{
|
||||
"id": "viaversion",
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"id": "packetevents",
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"id": "vpacketevents",
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"id": "fastlogin",
|
||||
"optional": true
|
||||
}
|
||||
],
|
||||
"main": "$main"
|
||||
|
||||
Reference in new issue
Block a user