Compare commits

..
4 Commits
Author SHA1 Message Date
Jones 595b1378ab Release 2.0.28 (#282) 2024-05-03 16:59:52 +02:00
AkkariinandMichel Elkenwaat 3012ed0d7a Add experimental Geyser support (#281)
* feat: added experimental geyser support.

* feat: send a special `PlayerAbilities` packet for the bedrock client
write 0x06 (`Flying` + `Allow Flying`) to prevent the bedrock clients falling during the `map-captcha` phase.
After testing, it has been confirmed that the bedrock client is unable to attempt to de-fly status. So just add it.

* fix: if the first move are success passed predicted. then don't reset tick.

* fix: tick skipping is allowed for bedrock clients.

* feat: fix limit & only set `bedrockSpawned` when client is bedrock.

* feat: make geyser checks configurable

* style: small codestyle improvements

* style: make code more human-readable

* fix: only check first packet if it's actually the first packet

* fix: [wip] completely exclude geyser from the gravity check [ci skip]

* style: fix comment

* chore: update `zh` language file. [ci skip]

* fix: Geyser users timing out because of a stupid mistake

* fix: only skip vehicle state in position check

* feat: add missing German translations [ci skip]

* feat: add missing Czech translations [ci skip]

* fix: make sure the vehicle check is recommended [ci skip]

* style: inline some stuff for better readability

* fix(geyser): completely skip gravity check for consistency [ci skip]

* fix(geyser): skip vehicle packet order check (UDP can mess this up)

* fix: add missing translations [ci skip]

---------

Co-authored-by: Michel Elkenwaat <admin@jonesdev.xyz>
2024-05-02 14:20:21 +02:00
Akkariin 4ccecde901 Add config & messages files for Chinese Simplified (#280)
* chore: add config & messages files for Chinese Simplified.

* style: rename `cn` to `zh`. [ci skip]
2024-04-28 19:16:55 +02:00
AkkariinandMichel Elkenwaat 87fcfb68f3 1.20.5 Support (#278)
* feat: support 1.20.5

* fix: vehicle & barrier block id

* fix: missing abilities & remove entities packet id

* refactor: merge RegistryBundlePacket into RegistryDataPacket

* style: clean up some code

* feat: implement transfer packet logic for 1.20.5+

* fix: check hostname before protocolVersion

* fix: transfer configuration

* fix: SetSlotPacket for 1.20.5

* chore: remove encoder/decoder debugging message. [ci skip]

* style: remove double empty line [ci skip]

---------

Co-authored-by: Michel Elkenwaat <admin@jonesdev.xyz>
2024-04-28 12:50:17 +02:00
27 changed files with 965 additions and 117 deletions

No files matched your search

+1 -1
View File
@@ -1,3 +1,3 @@
group=xyz.jonesdev.sonar
version=2.0.27
version=2.0.28
description=Effective Anti-bot plugin for Velocity, BungeeCord, and Bukkit (1.7-latest)
@@ -205,6 +205,15 @@ public final class SonarConfiguration {
verification.validLocaleRegex = Pattern.compile(generalConfig.getString("verification.checks.valid-locale-regex"));
verification.maxLoginPackets = clamp(generalConfig.getInt("verification.checks.max-login-packets"), 128, 8192);
verification.transfer.enabled = generalConfig.getBoolean("verification.transfer.enabled");
verification.transfer.host = generalConfig.getString("verification.transfer.destination-host");
verification.transfer.port = clamp(generalConfig.getInt("verification.transfer.destination-port"), 0, 0xffff);
if (verification.transfer.enabled) {
Sonar.get().getLogger().info("Transferring 1.20.5+ clients is enabled. Please make sure to follow the instructions in order for this to work properly.");
}
verification.checkGeyser = generalConfig.getBoolean("verification.check-geyser-players");
verification.logConnections = generalConfig.getBoolean("verification.log-connections");
verification.logDuringAttack = generalConfig.getBoolean("verification.log-during-attack");
verification.debugXYZPositions = generalConfig.getBoolean("verification.debug-xyz-positions");
@@ -443,6 +452,7 @@ public final class SonarConfiguration {
private final Gravity gravity = new Gravity();
private final Vehicle vehicle = new Vehicle();
private final Brand brand = new Brand();
private final Transfer transfer = new Transfer();
@Getter
public static final class Map {
@@ -497,6 +507,14 @@ public final class SonarConfiguration {
private Pattern validRegex;
}
@Getter
public static final class Transfer {
private boolean enabled;
private String host;
private int port;
}
private boolean checkGeyser;
private boolean logConnections;
private boolean logDuringAttack;
private boolean debugXYZPositions;
@@ -28,13 +28,19 @@ import java.net.InetAddress;
import java.util.UUID;
public interface FallbackUser {
@NotNull Channel getChannel();
@NotNull
Channel getChannel();
@NotNull InetAddress getInetAddress();
@NotNull
InetAddress getInetAddress();
@NotNull ProtocolVersion getProtocolVersion();
@NotNull
ProtocolVersion getProtocolVersion();
@NotNull FallbackUserState getState();
@NotNull
FallbackUserState getState();
boolean isGeyser();
void setState(final FallbackUserState state);
@@ -68,7 +68,8 @@ public enum ProtocolVersion {
MINECRAFT_1_19_4(762),
MINECRAFT_1_20(763),
MINECRAFT_1_20_2(764),
MINECRAFT_1_20_3(765);
MINECRAFT_1_20_3(765),
MINECRAFT_1_20_5(766);
private final int protocol;
@@ -93,14 +93,14 @@ public class FallbackChannelHandlerAdapter extends ChannelInboundHandlerAdapter
* @param protocol Protocol version number sent by the client
*/
protected final void handleHandshake(final @NotNull String hostname, final int protocol) throws Exception {
// Check if the player has already sent a handshake packet
if (protocolVersion != null) {
throw new CorruptedFrameException("Already sent handshake");
}
// Check if the hostname is invalid
if (hostname.isEmpty()) {
throw new CorruptedFrameException("Hostname is empty");
}
// Check if the player has already sent a handshake packet
if (protocolVersion != null) {
throw new CorruptedFrameException("Already sent handshake");
}
// Store the protocol version
protocolVersion = ProtocolVersion.fromId(protocol);
// Hook the traffic listener
@@ -153,8 +153,9 @@ public class FallbackChannelHandlerAdapter extends ChannelInboundHandlerAdapter
return;
}
// Completely skip Geyser connections
if (GeyserUtil.isGeyserConnection(channel, socketAddress)) {
// Completely skip Geyser connections if configured
final boolean geyser = GeyserUtil.isGeyserConnection(channel, socketAddress);
if (geyser && !Sonar.get().getConfig().getVerification().isCheckGeyser()) {
initialLogin(ctx, loginPacket, encoder, handler);
return;
}
@@ -212,7 +213,7 @@ public class FallbackChannelHandlerAdapter extends ChannelInboundHandlerAdapter
}
// Create an instance for the Fallback connection
user = new FallbackUserWrapper(channel, inetAddress, protocolVersion);
user = new FallbackUserWrapper(channel, inetAddress, protocolVersion, geyser);
// Let the verification handler take over the channel
user.hijack(username, offlineUUID, encoder, decoder, timeout, handler);
});
@@ -55,16 +55,19 @@ public final class FallbackUserWrapper implements FallbackUser {
private final ChannelPipeline pipeline;
private final InetAddress inetAddress;
private final ProtocolVersion protocolVersion;
private final boolean geyser;
@Setter
private FallbackUserState state = FallbackUserState.LOGIN_ACK; // 1.20.2+
public FallbackUserWrapper(final @NotNull Channel channel,
final @NotNull InetAddress inetAddress,
final @NotNull ProtocolVersion protocolVersion) {
final @NotNull ProtocolVersion protocolVersion,
final boolean geyser) {
this.channel = channel;
this.pipeline = channel.pipeline();
this.inetAddress = inetAddress;
this.protocolVersion = protocolVersion;
this.geyser = geyser;
}
@Override
@@ -78,7 +78,7 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
private int captchaTriesLeft;
// Cached options to make sure the original values don't change throughout the verification
private final boolean performVehicle, performCaptcha, performGravity, performCollisions;
private final boolean performVehicle, performCaptcha, performGravity, performCollisions, transfer;
public FallbackVerificationHandler(final @NotNull FallbackUser user,
final @NotNull String username,
@@ -86,17 +86,26 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
this.user = user;
this.username = username;
this.playerUuid = playerUuid;
this.performGravity = Sonar.get().getConfig().getVerification().getGravity().isEnabled();
// We don't want to check Geyser players for valid gravity, as this might cause issues because of the protocol
this.performGravity = !user.isGeyser() && Sonar.get().getConfig().getVerification().getGravity().isEnabled();
this.performCollisions = Sonar.get().getConfig().getVerification().getGravity().isCheckCollisions();
this.performCaptcha = FALLBACK.shouldPerformCaptcha();
this.performVehicle = FALLBACK.shouldPerformVehicleCheck();
this.transfer = Sonar.get().getConfig().getVerification().getTransfer().isEnabled()
&& user.getProtocolVersion().compareTo(MINECRAFT_1_20_5) >= 0;
}
private void configure() {
// Set the state to CONFIGURE to avoid false positives
user.setState(CONFIGURE);
// Send the necessary configuration packets to the client
user.delayedWrite(REGISTRY_SYNC);
if (user.getProtocolVersion().compareTo(MINECRAFT_1_20_5) >= 0) {
for (final FallbackPacket packet : REGISTRY_SYNC_1_20_5) {
user.delayedWrite(packet);
}
} else {
user.delayedWrite(REGISTRY_SYNC_LEGACY);
}
user.delayedWrite(FINISH_CONFIGURATION);
// Send all packets in one flush
user.getChannel().flush();
@@ -338,7 +347,7 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
// pre-1.9 clients do not have a PaddleBoat packet
if (user.getProtocolVersion().compareTo(MINECRAFT_1_9) < 0) {
receivedSteerBoat = true;
} else {
} else if (!user.isGeyser()) {
// PaddleBoat → PlayerInput → PaddleBoat
checkFrame(receivedSteerBoat, "invalid vehicle steering order");
}
@@ -361,8 +370,10 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
assertState(CONFIGURE);
// Check if the client has already sent valid ClientSettings and PluginMessage packets
checkFrame(resolvedClientBrand, "did not resolve client brand");
checkFrame(resolvedClientSettings, "did not resolve client settings");
if (!user.isGeyser()) {
checkFrame(resolvedClientBrand, "did not resolve client brand");
checkFrame(resolvedClientSettings, "did not resolve client settings");
}
// Start initializing the actual join process
updateEncoderDecoderState(FallbackPacketRegistry.GAME);
@@ -474,15 +485,15 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
// Reset all values to ensure safety on teleport
tick = 1;
posY = lastY = -1;
expectedTeleportId = -1;
posY = lastY = expectedTeleportId = -1;
// Now we can send the chunk data
sendChunkData();
return;
}
if (user.getState() != LOGIN_ACK) {
// Only handle position packets if we aren't in certain phases
if (user.getState() != LOGIN_ACK && user.getState() != VEHICLE) {
if (packet instanceof PlayerPositionPacket) {
final PlayerPositionPacket position = (PlayerPositionPacket) packet;
handlePositionUpdate(position.getX(), position.getY(), position.getZ(), position.isOnGround());
@@ -511,8 +522,7 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
&& x == SPAWN_X_POSITION && y == spawnYPosition && z == SPAWN_Z_POSITION) {
// Reset all values to ensure safety on teleport
tick = 1;
posY = -1;
expectedTeleportId = -1;
posY = expectedTeleportId = -1;
// Check for ground state in the first packet
checkFrame(!ground, "invalid ground state");
}
@@ -585,7 +595,7 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
// Make sure we don't run out of predicted Y motions
checkFrame(tick < preparedCachedYMotions.length, "too many movements");
if (!ground) {
if (!ground && performGravity) {
final double predictedY = preparedCachedYMotions[tick];
final double offsetY = Math.abs(deltaY - predictedY);
@@ -596,7 +606,7 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
}
// Check if the y motion is roughly equal to the predicted value
checkFrame(offsetY < 0.005, String.format("invalid gravity: %d, %.7f, %.10f, %.10f != %.10f",
checkFrame(offsetY < 5e-3, String.format("invalid gravity: %d, %.7f, %.10f, %.10f != %.10f",
tick, y, offsetY, deltaY, predictedY));
}
tick++;
@@ -662,7 +672,7 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
captchaTriesLeft = Sonar.get().getConfig().getVerification().getMap().getMaxTries();
// Set slot to map
user.delayedWrite(new SetSlotPacket(0, 36, 1, 0,
user.delayedWrite(new SetSlotPacket(36, 1,
ItemType.FILLED_MAP.getId(user.getProtocolVersion()), SetSlotPacket.MAP_NBT));
// Send random captcha to the player
captcha = MAP_INFO_PREPARER.getRandomCaptcha();
@@ -670,7 +680,7 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
// Teleport the player to the position above the platform
user.delayedWrite(CAPTCHA_POSITION);
// Make sure the player cannot move
user.delayedWrite(CAPTCHA_ABILITIES);
user.delayedWrite(user.isGeyser() ? CAPTCHA_ABILITIES_BEDROCK : CAPTCHA_ABILITIES);
// Make sure the player knows what to do
user.delayedWrite(enterCodeMessage);
// Send all packets in one flush
@@ -692,6 +702,13 @@ public final class FallbackVerificationHandler implements FallbackPacketListener
// Call the VerifySuccessEvent for external API usage
Sonar.get().getEventManager().publish(new UserVerifySuccessEvent(username, playerUuid, user, login.delay()));
// If enabled, transfer the player back to the origin server
// This feature was introduced by Mojang in Minecraft version 1.20.5
// Thanks a lot to @Kadeluxe for helping me out with this!
if (transfer) {
user.write(transferToOrigin);
}
// Disconnect player with the verification success message
user.disconnect(Sonar.get().getConfig().getVerification().getVerificationSuccess());
@@ -101,7 +101,7 @@ public final class FallbackPacketDecoder extends ChannelInboundHandlerAdapter {
}
}
private void doLengthSanityChecks(final ByteBuf byteBuf,
private void doLengthSanityChecks(final @NotNull ByteBuf byteBuf,
final @NotNull FallbackPacket packet) throws Exception {
final int expectedMaxLen = packet.expectedMaxLength(byteBuf, user.getProtocolVersion());
if (expectedMaxLen != -1 && byteBuf.readableBytes() > expectedMaxLen) {
@@ -51,22 +51,30 @@ public enum FallbackPacketRegistry {
CONFIG {
{
clientbound.register(DisconnectPacket.class, DisconnectPacket::new,
map(0x01, MINECRAFT_1_20_2, true));
map(0x01, MINECRAFT_1_20_2, true),
map(0x02, MINECRAFT_1_20_5, true));
clientbound.register(FinishConfigurationPacket.class, FinishConfigurationPacket::new,
map(0x02, MINECRAFT_1_20_2, false));
map(0x02, MINECRAFT_1_20_2, false),
map(0x03, MINECRAFT_1_20_5, false));
clientbound.register(KeepAlivePacket.class, KeepAlivePacket::new,
map(0x03, MINECRAFT_1_20_2, false));
map(0x03, MINECRAFT_1_20_2, false),
map(0x04, MINECRAFT_1_20_5, false));
clientbound.register(RegistryDataPacket.class, RegistryDataPacket::new,
map(0x05, MINECRAFT_1_20_2, true));
map(0x05, MINECRAFT_1_20_2, true),
map(0x07, MINECRAFT_1_20_5, true));
serverbound.register(ClientSettingsPacket.class, ClientSettingsPacket::new,
map(0x00, MINECRAFT_1_20_2, false));
serverbound.register(PluginMessagePacket.class, PluginMessagePacket::new,
map(0x01, MINECRAFT_1_20_2, false));
map(0x01, MINECRAFT_1_20_2, false),
map(0x02, MINECRAFT_1_20_5, false));
serverbound.register(FinishConfigurationPacket.class, FinishConfigurationPacket::new,
map(0x02, MINECRAFT_1_20_2, false));
map(0x02, MINECRAFT_1_20_2, false),
map(0x03, MINECRAFT_1_20_5, false));
serverbound.register(KeepAlivePacket.class, KeepAlivePacket::new,
map(0x03, MINECRAFT_1_20_2, false));
map(0x03, MINECRAFT_1_20_2, false),
map(0x04, MINECRAFT_1_20_5, false));
}
},
GAME {
@@ -84,7 +92,8 @@ public enum FallbackPacketRegistry {
map(0x25, MINECRAFT_1_19_1, true),
map(0x24, MINECRAFT_1_19_3, true),
map(0x28, MINECRAFT_1_19_4, true),
map(0x29, MINECRAFT_1_20_2, true));
map(0x29, MINECRAFT_1_20_2, true),
map(0x2B, MINECRAFT_1_20_5, true));
clientbound.register(KeepAlivePacket.class, KeepAlivePacket::new,
map(0x00, MINECRAFT_1_7_2, false),
map(0x1F, MINECRAFT_1_9, false),
@@ -98,7 +107,8 @@ public enum FallbackPacketRegistry {
map(0x20, MINECRAFT_1_19_1, false),
map(0x1F, MINECRAFT_1_19_3, false),
map(0x23, MINECRAFT_1_19_4, false),
map(0x24, MINECRAFT_1_20_2, false));
map(0x24, MINECRAFT_1_20_2, false),
map(0x26, MINECRAFT_1_20_5, false));
clientbound.register(DisconnectPacket.class, DisconnectPacket::new,
map(0x40, MINECRAFT_1_7_2, true),
map(0x1A, MINECRAFT_1_9, true),
@@ -112,7 +122,8 @@ public enum FallbackPacketRegistry {
map(0x19, MINECRAFT_1_19_1, true),
map(0x17, MINECRAFT_1_19_3, true),
map(0x1A, MINECRAFT_1_19_4, true),
map(0x1B, MINECRAFT_1_20_2, true));
map(0x1B, MINECRAFT_1_20_2, true),
map(0x1D, MINECRAFT_1_20_5, true));
clientbound.register(PlayerPositionLookPacket.class, PlayerPositionLookPacket::new,
map(0x08, MINECRAFT_1_7_2, true),
map(0x2E, MINECRAFT_1_9, true),
@@ -127,7 +138,8 @@ public enum FallbackPacketRegistry {
map(0x39, MINECRAFT_1_19_1, true),
map(0x38, MINECRAFT_1_19_3, true),
map(0x3C, MINECRAFT_1_19_4, true),
map(0x3E, MINECRAFT_1_20_2, true));
map(0x3E, MINECRAFT_1_20_2, true),
map(0x40, MINECRAFT_1_20_5, true));
clientbound.register(ClientAbilitiesPacket.class, ClientAbilitiesPacket::new,
map(0x39, MINECRAFT_1_7_2, true),
map(0x2B, MINECRAFT_1_9, true),
@@ -142,7 +154,8 @@ public enum FallbackPacketRegistry {
map(0x31, MINECRAFT_1_19_1, true),
map(0x30, MINECRAFT_1_19_3, true),
map(0x34, MINECRAFT_1_19_4, true),
map(0x36, MINECRAFT_1_20_2, true));
map(0x36, MINECRAFT_1_20_2, true),
map(0x38, MINECRAFT_1_20_5, true));
clientbound.register(ChunkDataPacket.class, ChunkDataPacket::new,
map(0x21, MINECRAFT_1_7_2, true),
map(0x20, MINECRAFT_1_9, true),
@@ -156,7 +169,8 @@ public enum FallbackPacketRegistry {
map(0x21, MINECRAFT_1_19_1, true),
map(0x20, MINECRAFT_1_19_3, true),
map(0x24, MINECRAFT_1_19_4, true),
map(0x25, MINECRAFT_1_20_2, true));
map(0x25, MINECRAFT_1_20_2, true),
map(0x27, MINECRAFT_1_20_5, true));
clientbound.register(RemoveEntitiesPacket.class, RemoveEntitiesPacket::new,
map(0x13, MINECRAFT_1_7_2, true),
map(0x30, MINECRAFT_1_9, true),
@@ -173,7 +187,8 @@ public enum FallbackPacketRegistry {
map(0x3A, MINECRAFT_1_19_3, true),
map(0x3E, MINECRAFT_1_19_4, true),
map(0x3E, MINECRAFT_1_19_4, true),
map(0x40, MINECRAFT_1_20_2, true));
map(0x40, MINECRAFT_1_20_2, true),
map(0x42, MINECRAFT_1_20_5, true));
clientbound.register(SetPassengersPacket.class, SetPassengersPacket::new,
map(0x1B, MINECRAFT_1_7_2, true),
map(0x40, MINECRAFT_1_9, true),
@@ -187,7 +202,8 @@ public enum FallbackPacketRegistry {
map(0x55, MINECRAFT_1_19_3, true),
map(0x59, MINECRAFT_1_19_4, true),
map(0x5B, MINECRAFT_1_20_2, true),
map(0x5D, MINECRAFT_1_20_3, true));
map(0x5D, MINECRAFT_1_20_3, true),
map(0x5F, MINECRAFT_1_20_5, true));
clientbound.register(SpawnEntityPacket.class, SpawnEntityPacket::new,
map(0x0E, MINECRAFT_1_7_2, true),
map(0x00, MINECRAFT_1_9, true),
@@ -205,7 +221,8 @@ public enum FallbackPacketRegistry {
map(0x3F, MINECRAFT_1_19_3, true),
map(0x43, MINECRAFT_1_19_4, true),
map(0x45, MINECRAFT_1_20_2, true),
map(0x47, MINECRAFT_1_20_3, true));
map(0x47, MINECRAFT_1_20_3, true),
map(0x49, MINECRAFT_1_20_5, true));
clientbound.register(TransactionPacket.class, TransactionPacket::new,
map(0x32, MINECRAFT_1_7_2, true),
map(0x11, MINECRAFT_1_9, true),
@@ -218,7 +235,8 @@ public enum FallbackPacketRegistry {
map(0x2F, MINECRAFT_1_19_1, true),
map(0x2E, MINECRAFT_1_19_3, true),
map(0x32, MINECRAFT_1_19_4, true),
map(0x33, MINECRAFT_1_20_2, true));
map(0x33, MINECRAFT_1_20_2, true),
map(0x35, MINECRAFT_1_20_5, true));
clientbound.register(DefaultSpawnPositionPacket.class, DefaultSpawnPositionPacket::new,
map(0x05, MINECRAFT_1_7_2, true),
map(0x43, MINECRAFT_1_9, true),
@@ -234,7 +252,8 @@ public enum FallbackPacketRegistry {
map(0x4C, MINECRAFT_1_19_3, true),
map(0x50, MINECRAFT_1_19_4, true),
map(0x52, MINECRAFT_1_20_2, true),
map(0x54, MINECRAFT_1_20_3, true));
map(0x54, MINECRAFT_1_20_3, true),
map(0x56, MINECRAFT_1_20_5, true));
clientbound.register(MapDataPacket.class, MapDataPacket::new,
map(0x34, MINECRAFT_1_7_2, true),
map(0x24, MINECRAFT_1_9, true),
@@ -247,7 +266,8 @@ public enum FallbackPacketRegistry {
map(0x26, MINECRAFT_1_19_1, true),
map(0x25, MINECRAFT_1_19_3, true),
map(0x29, MINECRAFT_1_19_4, true),
map(0x2A, MINECRAFT_1_20_2, true));
map(0x2A, MINECRAFT_1_20_2, true),
map(0x2C, MINECRAFT_1_20_5, true));
clientbound.register(SetSlotPacket.class, SetSlotPacket::new,
map(0x2F, MINECRAFT_1_7_2, true),
map(0x16, MINECRAFT_1_9, true),
@@ -273,9 +293,13 @@ public enum FallbackPacketRegistry {
map(0x60, MINECRAFT_1_19_3, false),
map(0x64, MINECRAFT_1_19_4, false),
map(0x67, MINECRAFT_1_20_2, false),
map(0x69, MINECRAFT_1_20_3, false));
map(0x69, MINECRAFT_1_20_3, false),
map(0x6C, MINECRAFT_1_20_5, false));
clientbound.register(GameEventPacket.class, GameEventPacket::new,
map(0x20, MINECRAFT_1_20_3, true));
map(0x20, MINECRAFT_1_20_3, true),
map(0x22, MINECRAFT_1_20_5, true));
clientbound.register(TransferPacket.class, TransferPacket::new,
map(0x73, MINECRAFT_1_20_5, true));
serverbound.register(PaddleBoatPacket.class, PaddleBoatPacket::new,
map(0x11, MINECRAFT_1_9, false),
@@ -290,7 +314,8 @@ public enum FallbackPacketRegistry {
map(0x18, MINECRAFT_1_19_3, false),
map(0x19, MINECRAFT_1_19_4, false),
map(0x1B, MINECRAFT_1_20_2, false),
map(0x1C, MINECRAFT_1_20_3, false));
map(0x1C, MINECRAFT_1_20_3, false),
map(0x1F, MINECRAFT_1_20_5, false));
serverbound.register(PlayerInputPacket.class, PlayerInputPacket::new,
map(0x0C, MINECRAFT_1_7_2, false),
map(0x15, MINECRAFT_1_9, false),
@@ -304,7 +329,8 @@ public enum FallbackPacketRegistry {
map(0x1E, MINECRAFT_1_19_3, false),
map(0x1F, MINECRAFT_1_19_4, false),
map(0x22, MINECRAFT_1_20_2, false),
map(0x23, MINECRAFT_1_20_3, false));
map(0x23, MINECRAFT_1_20_3, false),
map(0x26, MINECRAFT_1_20_5, false));
serverbound.register(UniversalChatPacket.class, UniversalChatPacket::new,
map(0x01, MINECRAFT_1_7_2, false),
map(0x02, MINECRAFT_1_9, false),
@@ -313,7 +339,8 @@ public enum FallbackPacketRegistry {
map(0x03, MINECRAFT_1_14, false),
map(0x04, MINECRAFT_1_19, false),
map(0x05, MINECRAFT_1_19_1, false),
map(0x05, MINECRAFT_1_19_3, false));
map(0x05, MINECRAFT_1_19_3, false),
map(0x06, MINECRAFT_1_20_5, false));
serverbound.register(KeepAlivePacket.class, KeepAlivePacket::new,
map(0x00, MINECRAFT_1_7_2, false),
map(0x0B, MINECRAFT_1_9, false),
@@ -328,7 +355,8 @@ public enum FallbackPacketRegistry {
map(0x11, MINECRAFT_1_19_3, false),
map(0x12, MINECRAFT_1_19_4, false),
map(0x14, MINECRAFT_1_20_2, false),
map(0x15, MINECRAFT_1_20_3, false));
map(0x15, MINECRAFT_1_20_3, false),
map(0x18, MINECRAFT_1_20_5, false));
serverbound.register(ClientSettingsPacket.class, ClientSettingsPacket::new,
map(0x15, MINECRAFT_1_7_2, false),
map(0x04, MINECRAFT_1_9, false),
@@ -339,7 +367,8 @@ public enum FallbackPacketRegistry {
map(0x08, MINECRAFT_1_19_1, false),
map(0x07, MINECRAFT_1_19_3, false),
map(0x08, MINECRAFT_1_19_4, false),
map(0x09, MINECRAFT_1_20_2, false));
map(0x09, MINECRAFT_1_20_2, false),
map(0x0A, MINECRAFT_1_20_5, false));
serverbound.register(PluginMessagePacket.class, PluginMessagePacket::new,
map(0x17, MINECRAFT_1_7_2, false),
map(0x09, MINECRAFT_1_9, false),
@@ -367,7 +396,8 @@ public enum FallbackPacketRegistry {
map(0x14, MINECRAFT_1_19_3, false),
map(0x15, MINECRAFT_1_19_4, false),
map(0x17, MINECRAFT_1_20_2, false),
map(0x18, MINECRAFT_1_20_3, false));
map(0x18, MINECRAFT_1_20_3, false),
map(0x1B, MINECRAFT_1_20_5, false));
serverbound.register(TeleportConfirmPacket.class, TeleportConfirmPacket::new,
map(0x00, MINECRAFT_1_9, false));
serverbound.register(PlayerPositionPacket.class, PlayerPositionPacket::new,
@@ -384,7 +414,8 @@ public enum FallbackPacketRegistry {
map(0x13, MINECRAFT_1_19_3, false),
map(0x14, MINECRAFT_1_19_4, false),
map(0x16, MINECRAFT_1_20_2, false),
map(0x17, MINECRAFT_1_20_3, false));
map(0x17, MINECRAFT_1_20_3, false),
map(0x1A, MINECRAFT_1_20_5, false));
serverbound.register(PlayerTickPacket.class, PlayerTickPacket::new,
map(0x03, MINECRAFT_1_7_2, false),
map(0x0F, MINECRAFT_1_9, false),
@@ -399,7 +430,8 @@ public enum FallbackPacketRegistry {
map(0x16, MINECRAFT_1_19_3, false),
map(0x17, MINECRAFT_1_19_4, false),
map(0x19, MINECRAFT_1_20_2, false),
map(0x1A, MINECRAFT_1_20_3, false));
map(0x1A, MINECRAFT_1_20_3, false),
map(0x1D, MINECRAFT_1_20_5, false));
serverbound.register(TransactionPacket.class, TransactionPacket::new,
map(0x0F, MINECRAFT_1_7_2, false),
map(0x05, MINECRAFT_1_9, false),
@@ -413,7 +445,8 @@ public enum FallbackPacketRegistry {
map(0x1F, MINECRAFT_1_19_3, false),
map(0x20, MINECRAFT_1_19_4, false),
map(0x23, MINECRAFT_1_20_2, false),
map(0x24, MINECRAFT_1_20_3, false));
map(0x24, MINECRAFT_1_20_3, false),
map(0x27, MINECRAFT_1_20_5, false));
}
};
@@ -23,11 +23,13 @@ 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.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.play.*;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
@UtilityClass
@@ -36,12 +38,14 @@ public class FallbackPreparer {
// 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);
// Chunks
public final FallbackPacket EMPTY_CHUNK_DATA = new ChunkDataPacket(0, 0);
// Finish Configuration
public final FallbackPacket FINISH_CONFIGURATION = new FinishConfigurationPacket();
// Synchronize Registry
public final FallbackPacket REGISTRY_SYNC = new RegistryDataPacket();
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);
// Keep Alive
public final FallbackPacket CAPTCHA_KEEP_ALIVE = new KeepAlivePacket(0L);
// Game Event (1.20.3+)
@@ -56,6 +60,8 @@ public class FallbackPreparer {
public FallbackPacket updateSectionBlocks;
// Default Spawn Position
public FallbackPacket dynamicSpawnPosition;
// Transfer packet
public FallbackPacket transferToOrigin;
// Disconnect messages
public FallbackPacket blacklisted;
@@ -150,6 +156,10 @@ public class FallbackPreparer {
MAP_INFO_PREPARER.prepare();
}
transferToOrigin = new TransferPacket(
Sonar.get().getConfig().getVerification().getTransfer().getHost(),
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);
@@ -78,6 +78,7 @@ public enum BlockType {
return 10225;
case MINECRAFT_1_20_2:
case MINECRAFT_1_20_3:
case MINECRAFT_1_20_5:
return 10366;
}
}, 1D);
@@ -72,6 +72,8 @@ public enum ItemType {
return 941;
case MINECRAFT_1_20_3:
return 979;
case MINECRAFT_1_20_5:
return 982;
}
});
@@ -18,26 +18,75 @@
package xyz.jonesdev.sonar.common.fallback.protocol.packets.config;
import io.netty.buffer.ByteBuf;
import lombok.Getter;
import lombok.ToString;
import lombok.*;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.CompoundBinaryTag;
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.dimension.DimensionRegistry;
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;
@Getter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public final class RegistryDataPacket implements FallbackPacket {
private CompoundBinaryTag tag;
private String type;
private List<RegistryDataPacket.Bundle> bundles;
@Override
public void encode(final @NotNull ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
writeNamelessCompoundTag(byteBuf, DimensionRegistry.CODEC_1_20);
public void encode(final @NotNull ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) {
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_20_5) < 0) {
writeNamelessCompoundTag(byteBuf, tag);
} else {
writeString(byteBuf, type);
writeVarInt(byteBuf, bundles.size());
for (final RegistryDataPacket.Bundle bundle : bundles) {
writeString(byteBuf, bundle.getName());
// Write the bundle tag
final CompoundBinaryTag tag = bundle.getTag();
if (tag != null) {
byteBuf.writeBoolean(true);
writeNamelessCompoundTag(byteBuf, tag);
} else {
byteBuf.writeBoolean(false);
}
}
}
}
@Override
public void decode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
throw new UnsupportedOperationException();
}
public static @NotNull List<FallbackPacket> of(final @NotNull CompoundBinaryTag rootTag) {
final List<FallbackPacket> packets = new ArrayList<>();
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));
}
return packets;
}
@Data
@Value
public static class Bundle {
@NotNull String name;
// In the protocol, it is able to be null
@Nullable CompoundBinaryTag tag;
}
}
@@ -58,6 +58,9 @@ public final class LoginSuccessPacket implements FallbackPacket {
if (protocolVersion.compareTo(MINECRAFT_1_19) >= 0) {
writeVarInt(byteBuf, 0); // properties
}
if (protocolVersion.compareTo(MINECRAFT_1_20_5) >= 0) {
byteBuf.writeBoolean(false); // should authenticate
}
}
@Override
@@ -30,7 +30,7 @@ import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class ClientAbilitiesPacket implements FallbackPacket {
public final class ClientAbilitiesPacket implements FallbackPacket {
private int encodedFlags;
private float flySpeed, walkSpeed;
@@ -210,7 +210,7 @@ public final class JoinGamePacket implements FallbackPacket {
return;
}
if (protocolVersion.compareTo(MINECRAFT_1_20_2) >= 0) {
if (protocolVersion.inBetween(MINECRAFT_1_20_2, MINECRAFT_1_20_3)) {
byteBuf.writeBoolean(isHardcore);
writeStringArray(byteBuf, levelNames);
writeVarInt(byteBuf, 0); // max players
@@ -228,6 +228,29 @@ public final class JoinGamePacket implements FallbackPacket {
byteBuf.writeBoolean(false); // flat
byteBuf.writeBoolean(false); // no last death location
writeVarInt(byteBuf, 0);
return;
}
if (protocolVersion.compareTo(MINECRAFT_1_20_5) >= 0) {
byteBuf.writeBoolean(isHardcore);
writeStringArray(byteBuf, levelNames);
writeVarInt(byteBuf, 0); // max players
writeVarInt(byteBuf, viewDistance);
writeVarInt(byteBuf, viewDistance); // simulation distance
byteBuf.writeBoolean(reducedDebugInfo);
byteBuf.writeBoolean(showRespawnScreen);
byteBuf.writeBoolean(false); // limited crafting
// It depends on the dimension data we send in RegistryData. If we only send one dimension. It should be 0.
writeVarInt(byteBuf, 0);
writeString(byteBuf, levelName);
byteBuf.writeLong(partialHashedSeed);
byteBuf.writeByte(gamemode);
byteBuf.writeByte(-1); // previous gamemode
byteBuf.writeBoolean(false); // debug type
byteBuf.writeBoolean(false); // flat
byteBuf.writeBoolean(false); // no last death location
writeVarInt(byteBuf, 0); // pearl cooldown
byteBuf.writeBoolean(false); // secure profile
}
}
@@ -38,10 +38,8 @@ import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.writeVarInt;
@NoArgsConstructor
@AllArgsConstructor
public final class SetSlotPacket implements FallbackPacket {
private int windowId;
private int slot;
private int count;
private int data;
private int itemId;
private CompoundBinaryTag compoundBinaryTag;
@@ -51,7 +49,7 @@ public final class SetSlotPacket implements FallbackPacket {
@Override
public void encode(final @NotNull ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) {
byteBuf.writeByte(windowId);
byteBuf.writeByte(0); // windowId
if (protocolVersion.compareTo(MINECRAFT_1_17_1) >= 0) {
writeVarInt(byteBuf, 0);
@@ -59,31 +57,46 @@ public final class SetSlotPacket implements FallbackPacket {
byteBuf.writeShort(slot);
if (protocolVersion.compareTo(MINECRAFT_1_13_2) >= 0) {
if (protocolVersion.inBetween(MINECRAFT_1_13_2, MINECRAFT_1_20_3)) {
byteBuf.writeBoolean(true);
}
if (protocolVersion.compareTo(MINECRAFT_1_20_5) >= 0) {
writeVarInt(byteBuf, count);
}
if (protocolVersion.compareTo(MINECRAFT_1_13_2) < 0) {
byteBuf.writeShort(itemId);
} else {
writeVarInt(byteBuf, itemId);
}
byteBuf.writeByte(count);
if (protocolVersion.compareTo(MINECRAFT_1_20_5) < 0) {
byteBuf.writeByte(count);
}
if (protocolVersion.compareTo(MINECRAFT_1_13) < 0) {
byteBuf.writeShort(data);
byteBuf.writeShort(0); // data
}
if (protocolVersion.compareTo(MINECRAFT_1_17) < 0) {
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_8) < 0) {
if (protocolVersion.compareTo(MINECRAFT_1_8) < 0) {
byteBuf.writeShort(-1);
} else {
byteBuf.writeByte(0);
}
} else {
if (protocolVersion.compareTo(MINECRAFT_1_20_2) >= 0) {
writeNamelessCompoundTag(byteBuf, compoundBinaryTag);
} else {
if (protocolVersion.compareTo(MINECRAFT_1_20_2) < 0) {
writeCompoundTag(byteBuf, compoundBinaryTag);
} else if (protocolVersion.compareTo(MINECRAFT_1_20_5) < 0) {
writeNamelessCompoundTag(byteBuf, compoundBinaryTag);
} else { // 1.20.5
// component
writeVarInt(byteBuf, 1); // component count to add
writeVarInt(byteBuf, 0); // component count to remove
// single VarInt component
writeVarInt(byteBuf, 26); // map component
writeVarInt(byteBuf, 0); // map id
}
}
}
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2024 jones
*
* 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.utility.protocol.ProtocolUtil.writeString;
import static xyz.jonesdev.sonar.common.utility.protocol.VarIntUtil.writeVarInt;
@Getter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public final class TransferPacket implements FallbackPacket {
private String host;
private int port;
@Override
public void encode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) {
writeString(byteBuf, host);
writeVarInt(byteBuf, port);
}
@Override
public void decode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) {
throw new UnsupportedOperationException();
}
}
@@ -77,6 +77,8 @@ public enum EntityType {
case MINECRAFT_1_20_2:
case MINECRAFT_1_20_3:
return 9;
case MINECRAFT_1_20_5:
return 10;
}
});
@@ -98,7 +98,7 @@ public class ProtocolUtil {
return str;
}
public static void writeString(final ByteBuf byteBuf, final CharSequence str) {
public static void writeString(final ByteBuf byteBuf, final @NotNull CharSequence str) {
final int size = ByteBufUtil.utf8Bytes(str);
VarIntUtil.writeVarInt(byteBuf, size);
byteBuf.writeCharSequence(str, StandardCharsets.UTF_8);
@@ -89,12 +89,12 @@ verification:
# - ADVENTURE: všechny komponenty UI jsou zobrazeny
gamemode: ADVENTURE
# Checks if the client sends proper packets when mounting a vehicle
# Kontroluje, zda klient odesílá správné pakety při montáži vozidla
vehicle:
# Possible types: ALWAYS, DURING_ATTACK, NEVER
# - ALWAYS: New players will always be mounted on a vehicle
# - DURING_ATTACK: New players will only be mounted on a vehicle during an attack
# - NEVER: New players will never be mounted on a vehicle (Recommended)
# Typy: ALWAYS, DURING_ATTACK, NEVER
# - ALWAYS: Noví hráči budou vždy namontováni na vozidle (Doporučeno)
# - DURING_ATTACK: Noví hráči budou namontováni na vozidlo pouze během útoku
# - NEVER: Noví hráči nebudou nikdy namontováni na vozidlo
timing: ALWAYS
# Nechá hráče verifikovat pomocí kódu na mapě ve hře
@@ -105,21 +105,21 @@ 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
# Options for the effects on the image shown to the player
# Možnosti efektů na obrázek zobrazený přehrávači
effects:
# Adds a glow effect to the image
# Přidá do obrázku efekt záře
flare: true
# Adds random lines to the image
# Přidá do obrázku náhodné čáry
scratches: true
# Applies a ripple (sine) distortion filter
# Přidá filtr zvlnění (sinus) zkreslení
ripple: true
# Applies a horizontal distortion filter
# Přidá horizontální filtr zkreslení
smear: true
# Adds a bit of blurriness/distortion to the image
# Dodává obrazu trochu rozmazání/zkreslení
pinch: true
# Applies color correction (color saturation)
# Použije korekci barev (sytost barev)
saturation: 0.15
# Applies a horizontal triangular ripple filter
# Přidá horizontální trojúhelníkový filtr zvlnění
distortion: 2
# Kolik odpovědí by měl Sonar předgenerovat?
# Tento úkol běží souběžně v pozadí
@@ -130,7 +130,7 @@ verification:
# 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
# It is not recommended to add numbers or letters that might look like one another
# 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:
@@ -148,9 +148,9 @@ verification:
# Maximální délka "client brand" při verifikaci
max-length: 64
# Regex for validování jmen při verifikaci
# Regex pro validování jmen při verifikaci
valid-name-regex: ^[a-zA-Z0-9_]+$
# Regex for validaci "client locale" při verifikaci
# Regex pro validaci "client locale" při verifikaci
valid-locale-regex: ^[a-zA-Z_]+$
# Maximální odezva hráče aby byl odpojen
# (Hodnota udávaná v milisekundách: 1 sekunda = 1000 milisekund)
@@ -158,6 +158,24 @@ verification:
# Maximální počet login packetů, který musí hřáč poslat aby byl vyhozen
max-login-packets: 256
# Je-li povoleno, přehrávač bude přenesen zpět na původní server
# po úspěšném absolvování ověření robota.
# Tuto funkci představil Mojang ve verzi Minecraft 1.20.5
transfer:
# Měl by Sonar přenést hráče na původní server (místo toho, aby je kopl)?
# Aby to fungovalo, musíte tuto funkci povolit v konfiguraci vašeho serveru
# Kromě toho možná budete chtít snížit množství omezení rychlosti přihlášení
# provádí Velocity nebo jiné proxy/pluginy, protože to může zabránit
# hráč nebyl převeden správně.
enabled: false
# Na který server by měl Sonar přenést přehrávač, když projde ověřením?
# Zadejte IP serveru, který hráči používají k běžnému připojení k vašemu serveru.
# Například můžete zadat "mc.hypixel.net" nebo přímou IP jako "1.1.1.1"
destination-host: "play.my-server.com"
# Jaký port by měl Sonar použít při přenosu přehrávače na původní server?
# Pokud váš server nepotřebuje port pro připojení, můžete toto ponechat jako 25565.
destination-port: 25565
# Měl by Sonar posílat nové verifikace?
log-connections: true
# Měl by Sonar posílat nové verifikace při útoku?
@@ -165,13 +183,17 @@ verification:
# Měl by Sonar posílat každý jeden pohyb při verifikaci?
# Toto nastavení není doporučeno pro servry v produkci, ale může být nápomocné při vyhledávaní chyb
debug-xyz-positions: false
# Měl by Sonar také kontrolovat hráče Geyser (Bedrock)?
# Tato funkce je experimentální a může způsobit problémy
# Pokud je toto zakázáno, hráči Bedrock budou přeskočeni
check-geyser-players: true
# Množství času který musí uběhnout aby hráč byl odpojen
# (Hodnota udávaná v milisekundách: 1 sekunda = 1000 milisekund)
read-timeout: 8000
# How long should a player wait before reconnecting during verification Jak dlouho by měl hráč
# Jak dlouho by měl hráč čekat, než se při ověřování znovu připojí
# (Hodnota udávaná v milisekundách: 1 sekunda = 1000 milisekund)
rejoin-delay: 8000
# How long should Sonar remember the amount of failed verifications for a player?
# Jak dlouho by si měl Sonar pamatovat počet neúspěšných ověření hráče?
# (Hodnota udávaná v milisekundách: 1 sekunda = 1000 milisekund)
remember-time: 120000
# Jak dlouho by měla být IP adresa zakázána k připojení po selhání verifikace mockrát?
@@ -86,12 +86,12 @@ verification:
# - ADVENTURE: alle UI-Komponenten sind sichtbar
gamemode: ADVENTURE
# Checks if the client sends proper packets when mounting a vehicle
# Überprüft, ob der Spieler beim Einsteigen in ein Boot die richtigen Pakete sendet
vehicle:
# Possible types: ALWAYS, DURING_ATTACK, NEVER
# - ALWAYS: New players will always be mounted on a vehicle
# - DURING_ATTACK: New players will only be mounted on a vehicle during an attack
# - NEVER: New players will never be mounted on a vehicle (Recommended)
# Mögliche Typen: ALWAYS, DURING_ATTACK, NEVER
# - ALWAYS: Neue Spieler werden immer auf einem Fahrzeug montiert (Empfohlen)
# - DURING_ATTACK: Neue Spieler werden nur während eines Angriffs auf ein Fahrzeug gesetzt
# - NEVER: Neue Spieler werden niemals in ein Fahrzeug eingebaut
timing: ALWAYS
# Lässt den Spieler einen Code von einer virtuellen Karte im Chat eingeben
@@ -102,21 +102,21 @@ verification:
# - DURING_ATTACK: Neue Spieler erhalten nur während eines Angriffs ein CAPTCHA
# - NEVER: Neue Spieler erhalten nie ein CAPTCHA (Empfohlen)
timing: NEVER
# Options for the effects on the image shown to the player
# Optionen für die Effekte auf das dem Player angezeigte Bild
effects:
# Adds a glow effect to the image
# Fügt dem Bild einen Leuchteffekt hinzu
flare: true
# Adds random lines to the image
# Fügt dem Bild zufällige Linien hinzu
scratches: true
# Applies a ripple (sine) distortion filter
# Wendet einen Sinus-Verzerrungsfilter an
ripple: true
# Applies a horizontal distortion filter
# Wendet einen horizontalen Verzerrungsfilter an
smear: true
# Adds a bit of blurriness/distortion to the image
# Fügt dem Bild etwas Unschärfe/Verzerrung hinzu
pinch: true
# Applies color correction (color saturation)
# Wendet eine Farbkorrektur an (Farbsättigung)
saturation: 0.15
# Applies a horizontal triangular ripple filter
# Wendet einen horizontalen dreieckigen Wellenfilter an
distortion: 2
# Wie viele Antworten soll Sonar vorbereiten?
# Dieser Vorgang erfolgt asynchron im Hintergrund
@@ -127,7 +127,7 @@ verification:
# 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
# It is not recommended to add numbers or letters that might look like one another
# 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:
@@ -155,6 +155,24 @@ verification:
# Maximale Anzahl an Login-Paketen, die der Spieler senden muss, um gekickt zu werden
max-login-packets: 256
# Wenn aktiviert, wird der Player zurück zum Ursprungsserver gesendet
# falls dieser die der Bot-Verifizierung erfolgreich besteht.
# Diese Funktion wurde von Mojang in Minecraft Version 1.20.5 eingeführt
transfer:
# Sollte Sonar den Spieler auf den Ursprungsserver senden (anstatt ihn zu kicken)?
# Damit dies funktioniert, müssen Sie die Funktion in der Konfiguration Ihres Servers aktivieren
# Darüber hinaus möchten Sie möglicherweise die Begrenzung der Anmelderate reduzieren
# wird von Velocity oder anderen Proxys/Plugins durchgeführt, da dies das verhindern könnte
# Spieler konnte nicht korrekt übertragen werden.
enabled: false
# Auf welchen Server soll Sonar den Spieler übertragen, wenn die Verifizierung bestanden wurde?
# Bitte geben Sie die Server-IP ein, die von Spielern verwendet wird, um sich zu verbinden.
# Sie können beispielsweise "mc.hypixel.net" oder eine direkte IP wie "1.1.1.1" verwenden.
destination-host: "play.my-server.com"
# Welchen Port sollte Sonar verwenden, wenn der Spieler auf den Ursprungsserver gesendet wird?
# Wenn Ihr Server keinen Port zum Herstellen einer Verbindung benötigt, können Sie diesen auf 25565 belassen.
destination-port: 25565
# Soll Sonar neue Überprüfungsversuche protokollieren?
log-connections: true
# Soll Sonar neue Überprüfungsversuche während Angriffen protokollieren?
@@ -162,6 +180,10 @@ verification:
# Soll Sonar jede einzelne Bewegung/Positionsänderung während der Überprüfung protokollieren?
# Dies wird für Produktivserver nicht empfohlen, kann aber beim Auffinden von Fehlern hilfreich sein.
debug-xyz-positions: false
# Sollte Sonar auch Geyser (Bedrock)-Spieler überprüfen?
# Diese Funktion ist experimentell und kann Probleme verursachen
# Wenn dies deaktiviert ist, werden Bedrock-Spieler übersprungen
check-geyser-players: true
# Zeit, die verstreichen muss, bevor ein Spieler ein Timeout erleidet
# (Dieser Wert stellt die Zeit in Millisekunden dar: 1 Sekunde = 1000 Millisekunden)
read-timeout: 8000
@@ -91,9 +91,9 @@ verification:
# 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
# - 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 (Recommended)
# - NEVER: New players will never be mounted on a vehicle
timing: ALWAYS
# Make the player type a code from a virtual map in chat
@@ -157,6 +157,24 @@ verification:
# Maximum number of login packets the player has to send in order to be kicked
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
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.
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"
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.
destination-port: 25565
# Should Sonar log new verification attempts?
log-connections: true
# Should Sonar log new verification attempts during attacks?
@@ -164,6 +182,10 @@ verification:
# Should Sonar log every single movement/position change during verification?
# This is not recommended for production servers but can be helpful for spotting errors.
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
check-geyser-players: true
# Amount of time that has to pass before a player times out
# (This value represents the time in milliseconds: 1 second = 1000 milliseconds)
read-timeout: 8000
@@ -91,9 +91,9 @@ verification:
# 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
# - 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 (Recommended)
# - NEVER: New players will never be mounted on a vehicle
timing: ALWAYS
# დააწერინეთ მოთამაშეს კოდი ჩატში, რომელიც იქნება ნაჩვენები ვირტუალურ მაპზე, რომელიც ხელში ეჭირებად
@@ -157,6 +157,24 @@ 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
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.
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"
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.
destination-port: 25565
# ჩაიწეროს თუ არა Sonar-მა ახალი ვერიფიკაციის მცდელობები?
log-connections: true
# ჩაწეროს თუ არა Sonar-მა ახალი ვერიფიკაციის მცდელობები შეტევის დროს?
@@ -164,6 +182,10 @@ 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
check-geyser-players: true
# დროის ოდენობა, რომელიც უნდა გავიდეს, სანამ მოთამაშე ტაიმაუტდება სერვერიდან.
# (ეს არის ნაჩვენები მილიწამებში: 1 წამი = 1000 მილიწამი)
read-timeout: 8000
@@ -91,9 +91,9 @@ verification:
# 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
# - 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 (Recommended)
# - NEVER: New players will never be mounted on a vehicle
timing: ALWAYS
# Заставить игрока ввести код с картинки в чат
@@ -157,6 +157,24 @@ 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
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.
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"
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.
destination-port: 25565
# Должен ли Sonar регистрировать новые попытки проверки?
log-connections: true
# Должен ли Sonar регистрировать новые попытки проверки во время атак?
@@ -164,6 +182,10 @@ 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
check-geyser-players: true
# Количество времени, которое должно пройти, прежде чем игрока кикнет.
# (Это значение представляет время в миллисекундах: 1 секунда = 1000 миллисекунд)
read-timeout: 8000
@@ -0,0 +1,201 @@
#
# /$$$$$$
# /$$__ $$
# | $$ \__/ /$$$$$$ /$$$$$$$ /$$$$$$ /$$$$$$
# | $$$$$$ /$$__ $$| $$__ $$ |____ $$ /$$__ $$
# \____ $$| $$ \ $$| $$ \ $$ /$$$$$$$| $$ \__/
# /$$ \ $$| $$ | $$| $$ | $$ /$$__ $$| $$
# | $$$$$$/| $$$$$$/| $$ | $$| $$$$$$$| $$
# \______/ \______/ |__/ |__/ \_______/|__/
#
# 有问题或需要帮助? https://jonesdev.xyz/discord
# 源代码: https://github.com/jonesdevelopment/sonar
# Copyright Jones Development and Sonar Contributors, licensed under GPL-3.0
#
# 未归类的设置
general:
# Sonar 应该在控制台中打印玩家的 IP 地址吗?
log-player-addresses: true
# 具有相同 IP 地址的在线玩家的最大数量
max-online-per-ip: 3
# 攻击检测器的敏感度设置
# 只有当您知道自己在做什么的时候才编辑它
attack-tracker:
# 在一段时间内至少需要 x 个新玩家加入才满足触发条件.
min-players-for-attack: 8
# 至少经过多少毫秒后才可以解除遭受攻击模式
# (该值为毫秒. 1秒等于1000毫秒.)
min-attack-duration: 30000
# 攻击检测器要重复检测到多少次攻击 才能确认服务器正在遭受攻击?
# 该值作为缓冲区 以避免可能的误判
min-attack-threshold: 2
# 当从遭受攻击标记为解除攻击时 应该需要等待多久才能再次被标记为正在遭受攻击?
# (该值为毫秒. 1秒等于1000毫秒.)
attack-cooldown-delay: 3000
# 您可以让 Sonar 连接到数据库 以便从数据库中保存和读取已经过验证的 IP 和玩家.
# 提示: IP 地址将以纯文本形式保存 请确保您的数据库安全!
database:
# 要连接到的数据库类型
# 可用类型: NONE (不使用数据库), MYSQL, MARIADB
type: NONE
# 数据库的地址
host: localhost
# 数据库的端口
port: 3306
# 数据库名称
name: sonar
# 用于登录数据库的用户名
username: ''
# 用于登录数据库的密码
password: ''
# Sonar 应该在数据库中保留在白名单中的玩家最多多少天?
maximum-age: 5
# 在遭到攻击时 每个新玩家的登录请求将排队 以避免大量连接同时进行验证
# 队列将每1秒刷新一次
queue:
# 每秒最多处理多少个登录请求?
max-polls: 30
# 每个需要验证的玩家都将被发送到一个轻量级的虚拟服务器来检查高级机器人
verification:
# Sonar 应该在什么时候验证玩家?
# 可用类型: ALWAYS, DURING_ATTACK, NEVER
# - ALWAYS: 玩家始终需要验证 直到他们通过验证为止
# - DURING_ATTACK: 玩家仅在服务器疑似遭到攻击时验证
# - NEVER: 玩家不会被验证
timing: ALWAYS
checks:
# 检查玩家的下落移动是否遵循原版客户端.
gravity:
# Sonar 是否应该检查玩家的下落移动? (推荐启用)
enabled: true
# Sonar 是否应该生成方块平台以检查玩家下落与方块之间的碰撞? (推荐启用)
check-collisions: true
# 当玩家验证失败时 是否向他们发送验证码?
captcha-on-fail: false
# 玩家跌落在平台上多少tick后才能被算作站在平台上?
max-movement-ticks: 8
# 玩家在虚拟服务器中的游戏模式
# 可用类型: SURVIVAL, CREATIVE, ADVENTURE
# - SURVIVAL: 生存模式, 所有UI可见
# - CREATIVE: 创造模式, 隐藏血量和饱食度
# - ADVENTURE: 冒险模式, 所有UI可见
gamemode: ADVENTURE
# 检查客户端坐在实体上时是否发送了正确的数据包
vehicle:
# 可用类型: ALWAYS, DURING_ATTACK, NEVER
# - ALWAYS: 始终应用该检查
# - DURING_ATTACK: 仅在疑似遭受攻击时应用该检查
# - NEVER: 永远不应用该检查
timing: ALWAYS
# 地图验证码
map-captcha:
# Sonar 应该在什么时候向玩家发送验证码?
# 可用类型: ALWAYS, DURING_ATTACK, NEVER
# - ALWAYS: 始终应用该检查
# - DURING_ATTACK: 仅在疑似遭受攻击时应用该检查
# - NEVER: 永远不应用该检查
timing: NEVER
# 图像效果选项 (用于混淆自动图像识别)
effects:
# 是否添加局部发光效果
flare: true
# 是否添加随机的线条
scratches: true
# 是否应用波纹失真效果
ripple: true
# 是否应用纵向畸变滤镜
smear: true
# 是否添加一些失真
pinch: true
# 图像的色彩饱和度
saturation: 0.15
# 水平三角形纹波滤镜
distortion: 2
# Sonar 应该一次准备多少个验证码?
precompute: 500
# Sonar 应该给玩家多少时间完成验证码? (该值为毫秒. 1秒等于1000毫秒.)
max-duration: 45000
# 玩家最多能输入多少次错误的验证码?
max-tries: 3
# 允许出现在验证码答案中的字符
# 不建议添加可能看起来彼此相似的数字或字母
dictionary: '1235689'
# Sonar 应该为验证码使用哪些字体?
font-names:
- Dialog
- DialogInput
- SansSerif
- Monospaced
# 检查玩家是否正在向服务器发送有效的客户端标识符
client-brand:
# 是否启用此检查? (建议启用)
enabled: true
# 用于检查客户端标识符的正则表达式
valid-regex: ^[!-~ ]+$
# 客户端标识符的最长长度限制
max-length: 64
# 用于检查用户名是否有效的正则表达式
valid-name-regex: ^[a-zA-Z0-9_]+$
# 用于检查客户端语言(例如 zh_CN)的正则表达式
valid-locale-regex: ^[a-zA-Z_]+$
# 玩家必须在多少毫秒内回复Ping?
max-ping: 10000
# 玩家最多能发送多少个数据包用于登录?
max-login-packets: 256
# 开启后 玩家将在验证成功后被重定向到指定的服务器
# 该功能由 Mojang 在 Minecraft 1.20.5 版本中引入
transfer:
# Sonar 是否应该将玩家重定向到指定的服务器而不是踢出玩家?
# 为此您必须在服务器软件的配置文件中允许接受来自transfer的连接.
# 此外 您可能希望减少 Velocity 或其它服务器软件/插件的登录速率限制
# 因为这可能会阻止玩家快速地重新连接到服务器
enabled: false
# 当玩家通过验证后, Sonar 应该将玩家转移到哪个服务器
# 请输入玩家正常连接服务器时使用的服务器IP或域名
# 例如使用域名("mc.hypixel.net") 或使用 IP 地址("1.1.1.1").
destination-host: "play.my-server.com"
# Sonar 应该告诉玩家通过以上域名的什么端口来连接到服务器?
# 如果服务器地址不包含端口 请保留25565.
destination-port: 25565
# 当 Sonar 尝试验证玩家时 是否在控制台中打印它?
log-connections: true
# Sonar 是否应该记录在攻击期间内的验证会话?
log-during-attack: false
# Sonar 是否应该记录验证过程中玩家的移动位置的变化?
# 有助于发现错误 但不建议在非调试环境下的服务器启用该选项.
debug-xyz-positions: false
# Sonar 是否应该检查来自 Geyser 的(基岩版)玩家?
# 此功能是实验性的. 请报告任何因为误判而导致无法通过检查的问题
# 如果关闭该选项 Sonar 将不检查这些玩家以便直接连接到服务器.
check-geyser-players: true
# 要在客户端未响应多少毫秒后因超时而被踢出服务器?
# (该值为毫秒. 1秒等于1000毫秒.)
read-timeout: 8000
# 当玩家在需要验证时(即还未通过检查的情况下)需要等待多少毫秒才可以重新连接?
# (该值为毫秒. 1秒等于1000毫秒.)
rejoin-delay: 8000
# 当玩家验证失败后, Sonar 应该记住他多久?
# (该值为毫秒. 1秒等于1000毫秒.)
remember-time: 120000
# 黑名单应在多少秒后过期? (可能由于频繁验证失败. 在黑名单中将会被拒绝进入服务器.)
# (该值为毫秒. 1秒等于1000毫秒.)
blacklist-time: 600000
# 玩家必须验证失败多少次才会被列入黑名单? (设置为0以完全禁用)
blacklist-threshold: 2
# Sonar 不应该检查哪些协议? (即 跳过检查)
# 您可以在 https://wiki.vg/Protocol_version_numbers 找到所有协议号
# 例如 Minecraft 1.20 对应的协议号为763.
whitelisted-protocols: []
# 黑名单中的协议将阻止所有使用该协议的客户端.
blacklisted-protocols: []
@@ -0,0 +1,305 @@
#
# /$$$$$$
# /$$__ $$
# | $$ \__/ /$$$$$$ /$$$$$$$ /$$$$$$ /$$$$$$
# | $$$$$$ /$$__ $$| $$__ $$ |____ $$ /$$__ $$
# \____ $$| $$ \ $$| $$ \ $$ /$$$$$$$| $$ \__/
# /$$ \ $$| $$ | $$| $$ | $$ /$$__ $$| $$
# | $$$$$$/| $$$$$$/| $$ | $$| $$$$$$$| $$
# \______/ \______/ |__/ |__/ \_______/|__/
#
# 有问题或需要帮助? https://jonesdev.xyz/discord
# 源代码: https://github.com/jonesdevelopment/sonar
# Copyright Jones Development and Sonar Contributors, licensed under GPL-3.0
#
# %prefix% 占位符
prefix: <b><yellow>Sonar</b> <gray>» <white>
# %support-url% 占位符
support-url: https://jonesdev.xyz/discord/
# %header% 占位符. (支持多行)
header:
- <b><yellow>Sonar</b><reset>
- ''
# %footer% 占位符. (支持多行)
footer:
- <gray>如果您认为这是一个错误, 请联系服务器管理员.
# 当玩家尝试加入但有太多在线玩家同时使用者玩家的 IP 地址时显示的踢出消息.
too-many-online-per-ip:
- '%header%'
- <red>您所使用的 IP 地址已经无法再登录更多账户了.
- '%footer%'
# 有关命令的语言配置.
commands:
# 当玩家尝试在没有权限的情况下运行 /sonar 时显示的消息
no-permission: '%prefix%<red>您没有足够的权限使用该命令.'
# 当命令用法不正确时显示的消息
incorrect-usage: '%prefix%<red>用法: /sonar %usage%'
# 当提供无效的 IP 地址时显示的消息
invalid-ip-address: '%prefix%无法正确识别您提供的IP地址 请检查输入是否有误.'
# 当控制台尝试执行只有玩家能执行的命令时显示的消息
player-only: '%prefix%<red>只有玩家才可以执行该命令.'
# 当玩家尝试执行只有控制台能执行的命令时显示的消息
console-only: '%prefix%<red>出于安全考虑 只允许通过控制台执行该命令.'
# 玩家执行Sonar命令的速度过快时显示的消息
cool-down: '%prefix%<red>您只能每 0.5 秒执行一次此命令.'
cool-down-left: '%prefix%<red>请等待 <bold>%time-left% 秒<reset><red> 后执行该命令.'
# 当玩家没有权限执行某个子命令时显示的消息
subcommand-no-permission: '%prefix%<red>您没有足够的权限使用该子命令. <gray>(%permission%)'
# 根命令
main:
# 执行根命令时显示在所有输出内容(由该命令输出的消息)上方的消息
header:
- <yellow>正在在 %platform% 平台上运行 <b>Sonar</b> %version%.
- <gray><click:open_url:'https://github.com/jonesdevelopment/sonar'>(C) %copyright_year%
Jones Development and Sonar Contributors</click>
- ''
- <gray>有问题或需要帮助?
- <white><click:open_url:'https://jonesdev.xyz/discord/'><hover:show_text:'(Click
to open Discord)'>在我们的Discord上打开一张ticket </hover></click><click:open_url:'https://github.com/jonesdevelopment/sonar/issues'><hover:show_text:'(Click
to open GitHub)'>或在Github打开新的问题.
- ''
# 执行根命令时 对于子命令的描述.
subcommands: '<click:suggest_command:''/sonar %subcommand% ''><hover:show_text:''<gray>只有玩家可以执行该命令:
</gray>%only_players%<br><gray>只有控制台可以执行该命令: </gray>%require_console%<br><gray>需要的权限:
</gray><white>%permission%<br><gray>别名: </gray>%aliases%''><dark_aqua>
▪ <gray>/sonar %subcommand% <white>%description%'
# 重新加载命令
reload:
# 当有人开始重载插件时显示的消息
start: '%prefix%正在重新加载 Sonar...'
# 插件完成重载时显示的消息
finish: '%prefix%<green>成功重新加载配置文件. <gray>(花费 %taken% 毫秒)'
# '/sonar verbose' 命令的消息
verbose:
# 玩家订阅详细信息时显示的消息
subscribed: '%prefix%已为您显示 Sonar 的详细信息.'
# 玩家取消订阅详细信息时显示的消息
unsubscribed: '%prefix%不再为您显示 Sonar 的详细信息.'
# '/sonar notify' 命令的消息
notify:
# 玩家订阅攻击通知时显示的消息
subscribed: '%prefix%已订阅攻击通知. 当服务器受到攻击时将会提醒您.'
# 玩家取消订阅攻击通知时显示的消息
unsubscribed: '%prefix%已退订攻击通知'
# '/sonar blacklist' 命令的消息
blacklist:
# 当尝试清理黑名单但黑名单中没有任何 IP 地址显示的消息
empty: '%prefix%黑名单中没有任何 IP 地址.'
# 当成功清理黑名单时显示的消息
cleared: '%prefix%您已成功从黑名单中删除 %removed% 个 IP 地址.'
# 当查看黑名单的大小时显示的消息
size: '%prefix%黑名单中目前有 %amount% 个 IP 地址.'
# 当手动将一个 IP 地址添加到黑名单时显示的消息
added: '%prefix%成功将 IP 地址 %ip% 列入黑名单.'
# 当尝试将白名单中的 IP 地址添加到黑名单中时显示的警告消息
added-warning: '%prefix%<red>警告: <white>IP 地址 %ip% 在白名单中. 添加到黑名单则意味着它将被同时从白名单中移除.'
# 当尝试从黑名单中移除一个 IP 地址时显示的消息
removed: '%prefix%成功将 IP 地址 %ip% 移出黑名单.'
# 当尝试向黑名单添加一个已经在黑名单中的 IP 地址时显示的消息
duplicate-ip: '%prefix%该 IP 地址已经在黑名单里了.'
# 当尝试移除一个不在黑名单中的 IP 地址时显示的消息
ip-not-found: '%prefix%您提供的 IP 地址并不在黑名单中.'
# '/sonar verified' 命令的消息
verified:
# 当尝试清除白名单但为空时显示的消息
empty: '%prefix%白名单中没有任何玩家.'
# 当成功清理白名单时显示的消息
cleared: '%prefix%您已成功从白名单中删除 %removed% 个玩家.'
# 当查看白名单的大小时显示的消息
size: '%prefix%白名单中目前有 %amount% 个已验证的玩家.'
# 当成功将一个 IP 地址内所有已验证的玩家移出白名单时显示的消息
removed: '%prefix%成功将所有使用 IP 地址 (%ip%) 的玩家移出白名单.'
# 当手动将一个 IP 地址添加到白名单中显示的消息
added: '%prefix%成功将 IP 地址 %ip% 列入白名单.'
# 当尝试添加一个已经在白名单中的 IP 地址和玩家的UUID 时显示的消息
already: '%prefix%您提供的 IP 地址和玩家的UUID已经在白名单中了.'
# 当尝试移除一个不在白名单中的 IP 地址时显示的消息
ip-not-found: '%prefix%您提供的 IP 地址不在白名单中.'
# 当操作冲突时(例如同时将一个 IP 地址移除白名单)显示的消息
blocked: '%prefix%请等待当前操作完成.'
# '/sonar statistics' 命令的消息
statistics:
# 执行该命令时显示在所有输出内容(由该命令输出的消息)上方的消息
header: '%prefix%当前会话中的 %type% 类型的统计信息:'
# 当查看一个未知的统计信息时显示的消息
unknown-type: '%prefix%<red>未知的统计信息! 可用的统计信息: <gray>%statistics%'
# 默认统计信息的消息
general:
- ' <dark_aqua>▪ <gray>白名单中的 IP 地址数量: <white>%verified%'
- ' <dark_aqua>▪ <gray>正在进行验证的 IP 地址数量: <white>%verifying%'
- ' <dark_aqua>▪ <gray>黑名单中的 IP 地址数量: <white>%blacklisted%'
- ' <dark_aqua>▪ <gray>缓冲队列中的等待登录的数量: <white>%queued%'
- ' <dark_aqua>▪ <gray>玩家加入次数: <white>%total_joins%'
- ' <dark_aqua>▪ <gray>总验证次数: <white>%total_attempts%'
- ' <dark_aqua>▪ <gray>验证失败的次数: <white>%total_failed%'
- ' <dark_aqua>▪ <gray>服务器当前会话的运行时间: <white>%uptime%'
# CPU 统计信息
cpu:
- ' <dark_aqua>▪ <gray>当前进程的 CPU 使用率: <white>%process_cpu%%'
- ' <dark_aqua>▪ <gray>当前系统的 CPU 使用率: <white>%system_cpu%%'
- ' <dark_aqua>▪ <gray>当前进程的多核平均使用率: <white>%average_process_cpu%%'
- ' <dark_aqua>▪ <gray>当前系统的多核平均使用率: <white>%average_system_cpu%%'
- ' <dark_aqua>▪ <gray>系统平均负载: <white>%load_average%%'
- ' <dark_aqua>▪ <gray>虚拟核心数量: <white>%virtual_cores%'
# 内存统计信息
memory:
- ' <dark_aqua>▪ <gray>可用内存: <white>%free_memory%'
- ' <dark_aqua>▪ <gray>已使用的内存: <white>%used_memory%'
- ' <dark_aqua>▪ <gray>最大可用内存: <white>%max_memory%'
- ' <dark_aqua>▪ <gray>已分配的内存: <white>%total_memory%'
# 网络统计信息
network:
- ' <dark_aqua>▪ <gray>当前下行: <white>%incoming%'
- ' <dark_aqua>▪ <gray>当前上行: <white>%outgoing%'
- ' <dark_aqua>▪ <gray>下行总量: <white>%ttl_incoming%'
- ' <dark_aqua>▪ <gray>上行总量: <white>%ttl_outgoing%'
# 验证过程中所有消息的翻译
verification:
# 验证过程中向控制台打印的调试信息
logs:
# 当玩家进入服务器并验证时显示的消息
connection: '%name%%ip% (%protocol%) 连接到了服务器.'
# 当玩家未通过检查时打印的消息
failed: '%ip% (%protocol%) 由于原因 %reason% 无法通过检查.'
# 当玩家被自动列入黑名单时打印的消息
blacklisted: '%ip% (%protocol%) 因太多次无法完成验证而被列入黑名单.'
# 当玩家完成验证时打印的消息
successful: '%name% 成功通过了验证 (耗时 %time% 秒!).'
# 当验证中的玩家需要解决验证码时显示的消息
captcha:
# 玩家需要输入验证码答案时显示的消息
enter-code: '%prefix%<green>请在聊天框中发送地图上的验证码.'
# 显示还有多少秒来解决验证码. (设置为空即禁用)
action-bar: '%prefix%<green>您还有 %time-left% 秒来解决该验证码.'
# 当玩家输入错误的验证码时显示的消息
incorrect: '%prefix%<red>您输入了错误的验证码. 请再试一次'
# 当验证码还在准备的时候显示的踢出消息
currently-preparing:
- '%header%'
- <yellow>反机器人还在准备所需的数据
- <gray>请稍后重新加入服务器
- '%footer%'
# 当玩家需要验证时重新连接速度太快时显示的踢出消息
too-fast-reconnect:
- '%header%'
- <gold>您的重新连接速度过快
- <gray>请稍后重新加入服务器
- '%footer%'
# 当玩家尝试加入但它的 IP 已经有另一位玩家在验证时显示的踢出消息
already-verifying:
- '%header%'
- <red>有另一位玩家正在使用您当前的 IP 地址进行验证
- <red>请稍后重新加入服务器
- '%footer%'
# 已经在排队等待验证的人加入时显示的踢出消息
already-queued:
- '%header%'
- <red>您的 IP 地址当前正在排队等待验证
- <red>请稍后重新加入服务器
- '%footer%'
# 当玩家尝试加入但暂时被列入黑名单时显示的踢出消息
blacklisted:
- '%header%'
- <red>您已被临时禁止加入服务器
- <red>请等待几分钟后重新加入服务器
- <gold>您认为您不应该被禁止? <gray>%support-url%
- '%footer%'
# 当玩家尝试加入 但他们的用户名包含不允许的字符时显示的踢出消息
invalid-username:
- '%header%'
- <red>您的用户名包含无效的字符. 请更改您的用户名 然后再试.
- '%footer%'
# 当玩家使用列入黑名单的版本加入时显示的踢出消息
blacklisted-protocol:
- '%header%'
- <red>您当前所使用的协议/客户端版本已被服务器禁止
- <gold>需要帮助以便您能登录服务器? <gray>%support-url%
- '%footer%'
# 当玩家验证成功时显示的踢出消息
success:
- '%header%'
- <green>您已成功通过验证
- <white>请重新连接以加入服务器
# 当玩家未能通过验证时显示的消息
failed:
- '%header%'
- <red>无法验证您是否是机器人
- <gray>请等待几秒后重新加入服务器以重新验证
- <gold>需要帮助? <gray>%support-url%
- '%footer%'
# 状态栏详细信息
verbose:
layout:
# 详细信息状态栏样式
# 占位符:
# - '%queued%' 正在排队的连接数数量
# - '%verifying%' 正在进行验证的连接数数量
# - '%blacklisted%' 黑名单大小
# - '%total-joins%' 玩家加入次数
# - '%verify-total%' 已执行的验证次数
# - '%verify-success%' 成功验证的 IP 的数量
# - '%verify-failed%' 验证失败的次数
# - '%logins-per-second%' 每秒登录服务器次数
# - '%connections-per-second%' 每秒接受连接次数
# - '%attack-duration%' 攻击持续时间
# - '%incoming-traffic%' 每秒下行带宽
# - '%outgoing-traffic%' 每秒上行带宽
# - '%incoming-traffic-ttl%' 已使用的下行带宽
# - '%outgoing-traffic-ttl%' 已使用的上行带宽
# - '%used-memory%' 已使用的内存
# - '%total-memory%' 已分配的内存
# - '%max-memory%' 最大可用内存
# - '%free-memory%' 可用内存
# - '%animation%' 可自定义的动画符号
# 默认的状态栏
normal: '%prefix%<gray>每秒连接数 <white>%connections-per-second% <dark_aqua>╺ <gray>每秒登录次数
<white>%logins-per-second% <dark_aqua>╺ <gray>正在验证的连接数 <white>%verifying% <dark_aqua>|
<green>⬆ <white>%outgoing-traffic%/s <red>⬇ <white>%incoming-traffic%/s <green><bold>%animation%<reset>'
# 遭受攻击时的状态栏
attack: '%prefix%<gray>每秒连接数 <white>%connections-per-second% <dark_aqua>╺ <gray>每秒登录次数
<white>%logins-per-second% <dark_aqua>╺ <gray>排队连接数 <white>%queued% <dark_aqua>╺
<gray>正在验证的连接数 <white>%verifying% <dark_aqua>╺ <gray>攻击持续时间 <white>%attack-duration%
<dark_aqua>| <green>⬆ <white>%outgoing-traffic%/s <red>⬇ <white>%incoming-traffic%/s <green><bold>%animation%<reset>'
# 动画占位符
# 其它预设:
# - ▙, ▛, ▜, ▟
# - ⬈, ⬊, ⬋, ⬉
animation:
- ◜
- ◝
- ◞
- ◟
# 攻击通知
notifications:
# 标题
title: <yellow><b>Sonar<reset>
# 子标题
subtitle: <gray>检测到活动的机器人攻击..
# 聊天消息
chat:
- ''
- <yellow><b>Sonar<reset>
- ''
- <gray>检测到活动的机器人攻击
- ' <dark_aqua>▪ <gray>每秒登录次数: <white>%logins-per-second%'
- ' <dark_aqua>▪ <gray>正在验证的连接数: <white>%verifying%'
- ' <dark_aqua>▪ <gray>排队连接数: <white>%queued%'
- ''
- <gray>使用 <white><click:run_command:'/sonar verbose'><hover:show_text:'(
- 点击执行)'>/sonar verbose</hover></click><gray> 或 <white><click:run_command:'/sonar
stats'><hover:show_text:'(点击执行)'>/sonar stats</hover></click><gray> 来查看更多信息.
- ''