mirror of
https://github.com/jonesdevelopment/sonar.git
synced 2026-11-05 09:20:15 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43a1aa2ae8 | ||
|
|
f36a613124 | ||
|
|
3e193bddd7 | ||
|
|
66d0651d1e | ||
|
|
b21c4e8a9a | ||
|
|
b77e0c4806 | ||
|
|
cb76dfe06e | ||
|
|
d0a758473f | ||
|
|
e4dfab87da | ||
|
|
2fe3ab1d8d | ||
|
|
4483e52852 | ||
|
|
22b41151d6 | ||
|
|
e849060db1 | ||
|
|
59b338e775 | ||
|
|
e33ee1333a | ||
|
|
50e6b017f8 | ||
|
|
f25cc18cb9 | ||
|
|
a7b1bfd958 | ||
|
|
d97f124201 | ||
|
|
5e9d8b4b29 | ||
|
|
c3eabd1265 | ||
|
|
9d2752dc32 | ||
|
|
5fe9c18268 | ||
|
|
f93dae9532 | ||
|
|
ab36823ca2 | ||
|
|
5c94f06510 | ||
|
|
d069b9aadb | ||
|
|
3337009fe4 | ||
|
|
b010e18552 | ||
|
|
82b4b3bac7 | ||
|
|
b143ea548a | ||
|
|
689e3ea36b | ||
|
|
c9c1a2abb7 | ||
|
|
a8aa284a5e | ||
|
|
448f8d9af3 | ||
|
|
3d8f61fce3 | ||
|
|
cf27a9fe84 | ||
|
|
bec6251b2a | ||
|
|
6497b89f10 | ||
|
|
525d67808e | ||
|
|
8864c0749a | ||
|
|
1ec4704f8e | ||
|
|
8a4937e3af | ||
|
|
f90446d4e3 | ||
|
|
66141815cb | ||
|
|
3fe7ccd65c | ||
|
|
f6203416d1 | ||
|
|
3c73ecf60f | ||
|
|
c116d7b556 | ||
|
|
057cda6c7a | ||
|
|
3a97a1f949 | ||
|
|
69b20769de | ||
|
|
9b87fde7c6 | ||
|
|
b939767ab9 | ||
|
|
d5a8cd5346 | ||
|
|
aa4e4662bb | ||
|
|
29187d4248 |
No files matched your search
@@ -0,0 +1 @@
|
||||
patreon: jonesdev
|
||||
@@ -48,6 +48,11 @@ Sonar also protects against spambot attacks since it queues the incoming connect
|
||||
Sonar is unlikely to ever falsely prevent a player from joining the server since Minecraft uses the TCP protocol, which means that packets are always sent in the correct order. Therefore, lag or ping should not affect the bot check.
|
||||
If you or one of your players experiences a false positive, make sure to report it by opening a [GitHub issue](https://github.com/jonesdevelopment/sonar/issues/new/choose) or a ticket on the [Discord server](https://jonesdev.xyz/discord/).
|
||||
|
||||
## Sponsors
|
||||
Massive thanks to the sponsors of Sonar who help keep this project running:
|
||||
|
||||
<a href="https://github.com/Hydoxl"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/107579333?v=4&h=50&w=50&fit=cover&mask=circle&maxage=7d" alt="logo" align="center"></a>
|
||||
|
||||
## Building
|
||||
If you want to build your own version of Sonar, please take a look at the [Sonar building documentation](https://docs.jonesdev.xyz/development/building).
|
||||
<br>
|
||||
|
||||
@@ -30,17 +30,15 @@ import xyz.jonesdev.sonar.api.update.UpdateChecker;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
public interface SonarCommand {
|
||||
List<String> TAB_SUGGESTIONS = new ArrayList<>();
|
||||
|
||||
Map<String, List<String>> ARG_TAB_SUGGESTIONS = new HashMap<>();
|
||||
Map<Integer, List<String>> TAB_SUGGESTIONS = new LinkedHashMap<>();
|
||||
|
||||
default void handle(final @NotNull InvocationSource source, final String @NotNull [] args) {
|
||||
// Check if the player actually has the permission to run the command
|
||||
if (source.isPlayer() && !source.getPermissionFunction().test("sonar.command")) {
|
||||
source.sendMessage(Sonar.get().getConfig().getNoPermission());
|
||||
if (Sonar.get().getConfig().getNoPermission() != null) {
|
||||
source.sendMessage(Sonar.get().getConfig().getNoPermission());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -103,29 +101,28 @@ public interface SonarCommand {
|
||||
}
|
||||
|
||||
static void prepareCachedTabSuggestions() {
|
||||
// Don't re-cache tab suggestions
|
||||
if (!TAB_SUGGESTIONS.isEmpty()) return;
|
||||
// Cache tab suggestions
|
||||
for (final Subcommand subcommand : Sonar.get().getSubcommandRegistry().getSubcommands()) {
|
||||
TAB_SUGGESTIONS.add(subcommand.getInfo().name());
|
||||
if (subcommand.getInfo().aliases().length > 0) {
|
||||
TAB_SUGGESTIONS.addAll(Arrays.asList(subcommand.getInfo().aliases()));
|
||||
}
|
||||
final List<String> arguments = Arrays.asList(subcommand.getInfo().arguments());
|
||||
ARG_TAB_SUGGESTIONS.put(subcommand.getInfo().name(), arguments);
|
||||
for (final String alias : subcommand.getInfo().aliases()) {
|
||||
ARG_TAB_SUGGESTIONS.put(alias, arguments);
|
||||
if (TAB_SUGGESTIONS.isEmpty()) {
|
||||
final List<String> subcommandsAndAliases = new ArrayList<>();
|
||||
for (final Subcommand subcommand : Sonar.get().getSubcommandRegistry().getSubcommands()) {
|
||||
subcommandsAndAliases.add(subcommand.getInfo().name());
|
||||
|
||||
final List<String> arguments = Arrays.asList(subcommand.getInfo().arguments());
|
||||
TAB_SUGGESTIONS.put(subcommand.getInfo().name().hashCode(), arguments);
|
||||
|
||||
for (final String alias : subcommand.getInfo().aliases()) {
|
||||
subcommandsAndAliases.add(alias);
|
||||
TAB_SUGGESTIONS.put(alias.hashCode(), arguments);
|
||||
}
|
||||
}
|
||||
TAB_SUGGESTIONS.put(-1, subcommandsAndAliases);
|
||||
}
|
||||
}
|
||||
|
||||
default List<String> getCachedTabSuggestions(final String @NotNull [] arguments) {
|
||||
if (arguments.length <= 1) {
|
||||
return TAB_SUGGESTIONS;
|
||||
} else if (arguments.length == 2) {
|
||||
final String subcommandName = arguments[0].toLowerCase();
|
||||
return ARG_TAB_SUGGESTIONS.getOrDefault(subcommandName, emptyList());
|
||||
if (arguments.length <= 2) {
|
||||
final int subcommandHash = arguments.length <= 1 ? -1 : arguments[0].toLowerCase().hashCode();
|
||||
return TAB_SUGGESTIONS.getOrDefault(subcommandHash, Collections.emptyList());
|
||||
}
|
||||
return emptyList();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.command.CommandInvocation;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
|
||||
import java.util.Objects;
|
||||
@@ -91,7 +90,7 @@ public abstract class Subcommand {
|
||||
}
|
||||
|
||||
// Execute the sub command from the invocation source with the given arguments
|
||||
execute(new CommandInvocation(invocationSource, arguments));
|
||||
execute(invocationSource, arguments);
|
||||
}
|
||||
|
||||
public final @NotNull String getDescription() {
|
||||
@@ -99,5 +98,5 @@ public abstract class Subcommand {
|
||||
return Objects.requireNonNull(Sonar.get().getConfig().getMessagesConfig().getString(path));
|
||||
}
|
||||
|
||||
protected abstract void execute(final @NotNull CommandInvocation invocation);
|
||||
protected abstract void execute(final @NotNull InvocationSource source, final String @NotNull [] args);
|
||||
}
|
||||
@@ -195,6 +195,7 @@ public final class SonarConfiguration {
|
||||
verification.brand.validRegex = Pattern.compile(generalConfig.getString("verification.checks.client-brand.valid-regex"));
|
||||
verification.brand.maxLength = generalConfig.getInt("verification.checks.client-brand.max-length");
|
||||
|
||||
verification.timeOfDay = clamp(generalConfig.getInt("verification.time-of-day"), 0, 24000);
|
||||
verification.gamemode = Verification.Gamemode.valueOf(generalConfig.getString("verification.gamemode"));
|
||||
verification.validNameRegex = Pattern.compile(generalConfig.getString("verification.checks.valid-name-regex"));
|
||||
verification.validLocaleRegex = Pattern.compile(generalConfig.getString("verification.checks.valid-locale-regex"));
|
||||
@@ -242,8 +243,14 @@ public final class SonarConfiguration {
|
||||
|
||||
// Messages
|
||||
prefix = MiniMessage.miniMessage().deserialize(messagesConfig.getString("prefix"));
|
||||
noPermission = MiniMessage.miniMessage().deserialize(messagesConfig.getString("commands.no-permission"),
|
||||
Placeholder.component("prefix", prefix));
|
||||
|
||||
final String noPermissionMessage = messagesConfig.getString("commands.no-permission");
|
||||
if (noPermissionMessage.isEmpty()) {
|
||||
noPermission = null;
|
||||
} else {
|
||||
noPermission = MiniMessage.miniMessage().deserialize(noPermissionMessage,
|
||||
Placeholder.component("prefix", prefix));
|
||||
}
|
||||
|
||||
SonarCommand.prepareCachedTabSuggestions();
|
||||
|
||||
@@ -259,6 +266,7 @@ public final class SonarConfiguration {
|
||||
|
||||
tooManyOnlinePerIp = deserializeDisconnectMessage("too-many-online-per-ip");
|
||||
verification.currentlyPreparing = deserializeDisconnectMessage("verification.currently-preparing");
|
||||
verification.unsupportedVersion = deserializeDisconnectMessage("verification.unsupported-version");
|
||||
verification.tooFastReconnect = deserializeDisconnectMessage("verification.too-fast-reconnect");
|
||||
verification.alreadyVerifying = deserializeDisconnectMessage("verification.already-verifying");
|
||||
verification.alreadyQueued = deserializeDisconnectMessage("verification.already-queued");
|
||||
@@ -408,6 +416,7 @@ public final class SonarConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
private int timeOfDay;
|
||||
private boolean checkGeyser;
|
||||
private boolean logConnections;
|
||||
private boolean logDuringAttack;
|
||||
@@ -432,6 +441,7 @@ public final class SonarConfiguration {
|
||||
private Component blacklisted;
|
||||
private Component protocolBlacklisted;
|
||||
private Component currentlyPreparing;
|
||||
private Component unsupportedVersion;
|
||||
}
|
||||
|
||||
@Getter
|
||||
|
||||
+49
-57
@@ -31,6 +31,7 @@ import org.jetbrains.annotations.Range;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.config.SonarConfiguration;
|
||||
import xyz.jonesdev.sonar.api.database.model.VerifiedPlayer;
|
||||
import xyz.jonesdev.sonar.api.fingerprint.FingerprintingUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.SQLException;
|
||||
@@ -45,8 +46,6 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public final class VerifiedPlayerController {
|
||||
private static final ExecutorService DB_UPDATE_SERVICE = Executors.newSingleThreadExecutor();
|
||||
|
||||
@Getter
|
||||
private final Set<String> cache = Collections.synchronizedSet(new HashSet<>());
|
||||
private @Nullable ConnectionSource connectionSource;
|
||||
@@ -54,6 +53,7 @@ public final class VerifiedPlayerController {
|
||||
private QueryBuilder<VerifiedPlayer, Integer> queryBuilder;
|
||||
@Getter
|
||||
private final @NotNull SonarConfiguration.Database.Type cachedDatabaseType;
|
||||
private final ExecutorService updateService = Executors.newSingleThreadExecutor();
|
||||
|
||||
public VerifiedPlayerController(final @NotNull LibraryManager libraryManager) {
|
||||
final SonarConfiguration.Database database = Sonar.get().getConfig().getDatabase();
|
||||
@@ -95,7 +95,6 @@ public final class VerifiedPlayerController {
|
||||
Sonar.get().getConfig().getGeneralConfig().getString("database.password"),
|
||||
cachedDatabaseType.getDatabaseType());
|
||||
|
||||
// Create database table
|
||||
try {
|
||||
TableUtils.createTableIfNotExists(connectionSource, VerifiedPlayer.class);
|
||||
} catch (SQLException ignored) {
|
||||
@@ -111,14 +110,16 @@ public final class VerifiedPlayerController {
|
||||
|
||||
// Make sure to run the clean task and the caching task in the same thread
|
||||
// https://github.com/jonesdevelopment/sonar/issues/150
|
||||
DB_UPDATE_SERVICE.execute(() -> {
|
||||
try {
|
||||
// Make sure to clear all outdated entries first
|
||||
clearOld(database.getMaximumAge());
|
||||
// Add all entries from the database to the cache
|
||||
dao.queryForAll().forEach(verifiedPlayer -> cache.add(verifiedPlayer.getFingerprint()));
|
||||
} catch (SQLException exception) {
|
||||
exception.printStackTrace(System.err);
|
||||
updateService.execute(() -> {
|
||||
if (connectionSource != null) {
|
||||
try {
|
||||
// Make sure to clear all outdated entries first
|
||||
clearOld(database.getMaximumAge());
|
||||
// Add all entries from the database to the cache
|
||||
dao.queryForAll().forEach(verifiedPlayer -> cache.add(verifiedPlayer.getFingerprint()));
|
||||
} catch (SQLException exception) {
|
||||
exception.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (SQLException exception) {
|
||||
@@ -137,16 +138,17 @@ public final class VerifiedPlayerController {
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace(System.err);
|
||||
}
|
||||
// Make sure not to reuse a closed connection
|
||||
connectionSource = null;
|
||||
updateService.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all old entries using the given timestamp.
|
||||
* Clear all old entries using the given timestamp
|
||||
*/
|
||||
private void clearOld(final @Range(from = 1, to = 365) int maximumAge) throws SQLException {
|
||||
final long timestamp = Instant.now()
|
||||
.minus(maximumAge, ChronoUnit.DAYS)
|
||||
.getEpochSecond() * 1000L; // convert to ms
|
||||
final long timestamp = Instant.now().minus(maximumAge, ChronoUnit.DAYS).getEpochSecond() * 1000L;
|
||||
|
||||
final List<VerifiedPlayer> oldEntries = queryBuilder.where()
|
||||
.lt("timestamp", new Timestamp(timestamp))
|
||||
@@ -175,34 +177,36 @@ public final class VerifiedPlayerController {
|
||||
return;
|
||||
}
|
||||
|
||||
DB_UPDATE_SERVICE.execute(() -> {
|
||||
// We cannot throw a NullPointerException within the executor service
|
||||
// because we want to handle the error instead of simply throwing an exception
|
||||
if (connectionSource == null) {
|
||||
return;
|
||||
}
|
||||
updateService.execute(() -> {
|
||||
if (connectionSource != null) {
|
||||
try {
|
||||
final List<VerifiedPlayer> verifiedPlayer = queryBuilder.where()
|
||||
.eq("fingerprint", fingerprint)
|
||||
.query();
|
||||
|
||||
try {
|
||||
final List<VerifiedPlayer> verifiedPlayer = queryBuilder.where()
|
||||
.eq("fingerprint", fingerprint)
|
||||
.query();
|
||||
|
||||
if (verifiedPlayer != null) {
|
||||
for (final VerifiedPlayer player : verifiedPlayer) {
|
||||
dao.delete(player);
|
||||
if (verifiedPlayer != null) {
|
||||
for (final VerifiedPlayer player : verifiedPlayer) {
|
||||
dao.delete(player);
|
||||
}
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
exception.printStackTrace(System.err);
|
||||
}
|
||||
} catch (SQLException exception) {
|
||||
exception.printStackTrace(System.err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* First, cache the player locally and then,
|
||||
* secondly, asynchronously add the player to the database.
|
||||
*
|
||||
* @param player VerifiedPlayer model
|
||||
* Creates a new VerifiedPlayer model from the given username, host address, and timestamp
|
||||
*/
|
||||
public void add(final @NotNull String username, final @NotNull String hostAddress, final long timestamp) {
|
||||
final String fingerprint = FingerprintingUtil.getFingerprint(username, hostAddress);
|
||||
// Add a new VerifiedPlayer object from the given parameters to the database
|
||||
add(new VerifiedPlayer(fingerprint, timestamp));
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches the player locally and then adds the player to the database
|
||||
*/
|
||||
public void add(final @NotNull VerifiedPlayer player) {
|
||||
cache.add(player.getFingerprint());
|
||||
@@ -212,39 +216,27 @@ public final class VerifiedPlayerController {
|
||||
return;
|
||||
}
|
||||
|
||||
DB_UPDATE_SERVICE.execute(() -> {
|
||||
// We cannot throw a NullPointerException within the executor service
|
||||
// because we want to handle the error instead of simply throwing an exception
|
||||
if (connectionSource == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
dao.createIfNotExists(player);
|
||||
} catch (SQLException exception) {
|
||||
exception.printStackTrace(System.err);
|
||||
updateService.execute(() -> {
|
||||
if (connectionSource != null) {
|
||||
try {
|
||||
dao.createIfNotExists(player);
|
||||
} catch (SQLException exception) {
|
||||
exception.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of verified IP addresses
|
||||
*
|
||||
* @return Estimated size of the local cache
|
||||
*/
|
||||
public int estimatedSize() {
|
||||
return cache.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the local cache, and, if the database type is set,
|
||||
* remove the table from the database.
|
||||
*/
|
||||
public synchronized void clearAll() {
|
||||
public void clearAll() {
|
||||
cache.clear();
|
||||
|
||||
// Delete the entire table from the database, if necessary
|
||||
if (cachedDatabaseType != SonarConfiguration.Database.Type.NONE) {
|
||||
if (connectionSource != null
|
||||
&& cachedDatabaseType != SonarConfiguration.Database.Type.NONE) {
|
||||
try {
|
||||
dao.deleteBuilder().delete();
|
||||
} catch (SQLException exception) {
|
||||
|
||||
@@ -20,7 +20,6 @@ package xyz.jonesdev.sonar.api.event.impl;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import xyz.jonesdev.sonar.api.event.SonarEvent;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
|
||||
@@ -28,6 +27,5 @@ import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
@ToString
|
||||
@RequiredArgsConstructor
|
||||
public final class UserBlacklistedEvent implements SonarEvent {
|
||||
@ApiStatus.Experimental
|
||||
private final FallbackUser user;
|
||||
}
|
||||
@@ -27,6 +27,5 @@ import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
@ToString
|
||||
@RequiredArgsConstructor
|
||||
public final class UserVerifyJoinEvent implements SonarEvent {
|
||||
private final String username;
|
||||
private final FallbackUser user;
|
||||
}
|
||||
@@ -22,12 +22,10 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import xyz.jonesdev.sonar.api.event.SonarEvent;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
import xyz.jonesdev.sonar.api.timer.SystemTimer;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@RequiredArgsConstructor
|
||||
public final class UserVerifySuccessEvent implements SonarEvent {
|
||||
private final FallbackUser user;
|
||||
private final SystemTimer loginTimer;
|
||||
}
|
||||
@@ -68,7 +68,8 @@ public enum ProtocolVersion {
|
||||
MINECRAFT_1_20_2(764),
|
||||
MINECRAFT_1_20_3(765),
|
||||
MINECRAFT_1_20_5(766),
|
||||
MINECRAFT_1_21(767);
|
||||
MINECRAFT_1_21(767),
|
||||
MINECRAFT_1_21_2(768);
|
||||
|
||||
private final int protocol;
|
||||
|
||||
@@ -90,6 +91,22 @@ public enum ProtocolVersion {
|
||||
return ID_TO_PROTOCOL_CONSTANT.getOrDefault(protocol, UNKNOWN);
|
||||
}
|
||||
|
||||
public boolean greaterThan(final ProtocolVersion that) {
|
||||
return compareTo(that) > 0;
|
||||
}
|
||||
|
||||
public boolean greaterThanOrEquals(final ProtocolVersion that) {
|
||||
return compareTo(that) >= 0;
|
||||
}
|
||||
|
||||
public boolean lessThan(final ProtocolVersion that) {
|
||||
return compareTo(that) < 0;
|
||||
}
|
||||
|
||||
public boolean lessThanOrEquals(final ProtocolVersion that) {
|
||||
return compareTo(that) <= 0;
|
||||
}
|
||||
|
||||
public boolean inBetween(final ProtocolVersion first, final ProtocolVersion last) {
|
||||
return compareTo(first) >= 0 && compareTo(last) <= 0;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ public final class AttackTracker {
|
||||
// An attack has been detected
|
||||
if (currentAttack == null) {
|
||||
currentAttack = new AttackStatistics();
|
||||
currentAttack.successfulVerifications = Sonar.get().getVerifiedPlayerController().estimatedSize();
|
||||
currentAttack.successfulVerifications = Sonar.get().getVerifiedPlayerController().getCache().size();
|
||||
currentAttack.failedVerifications = Sonar.get().getStatistics().getTotalFailedVerifications();
|
||||
Sonar.get().getEventManager().publish(new AttackDetectedEvent());
|
||||
Sonar.get().getChatNotificationHandler().handleNotification();
|
||||
@@ -118,7 +118,7 @@ public final class AttackTracker {
|
||||
final String endTimestamp = String.valueOf(System.currentTimeMillis() / 1000L);
|
||||
final long blacklisted = Sonar.get().getFallback().getBlacklist().estimatedSize();
|
||||
// Calculate during-attack-statistics using their deltas
|
||||
final long totalVerified = Sonar.get().getVerifiedPlayerController().estimatedSize();
|
||||
final long totalVerified = Sonar.get().getVerifiedPlayerController().getCache().size();
|
||||
final long verified = Math.max(totalVerified - currentAttack.successfulVerifications, 0);
|
||||
final long totalFailed = Sonar.get().getStatistics().getTotalFailedVerifications();
|
||||
final long failed = Math.max(totalFailed - currentAttack.failedVerifications, 0);
|
||||
|
||||
@@ -27,10 +27,9 @@ import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.api.command.SonarCommand;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
public final class BukkitSonarCommand implements CommandExecutor, TabExecutor, SonarCommand {
|
||||
|
||||
@Override
|
||||
@@ -54,9 +53,6 @@ public final class BukkitSonarCommand implements CommandExecutor, TabExecutor, S
|
||||
final String commandAlias,
|
||||
final String @NotNull [] args) {
|
||||
// Do not allow tab completion if the player does not have the required permission
|
||||
if (!sender.hasPermission("sonar.command")) {
|
||||
return emptyList();
|
||||
}
|
||||
return getCachedTabSuggestions(args);
|
||||
return sender.hasPermission("sonar.command") ? getCachedTabSuggestions(args) : Collections.emptyList();
|
||||
}
|
||||
}
|
||||
+7
-6
@@ -28,6 +28,7 @@ import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketRegistry;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.handshake.HandshakePacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.login.LoginStartPacket;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
import xyz.jonesdev.sonar.common.util.exception.QuietDecoderException;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
@@ -35,8 +36,6 @@ import java.util.Objects;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.FallbackPipelines.FALLBACK_INBOUND_HANDLER;
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.packets.handshake.HandshakePacket.*;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.DEBUG;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.readVarInt;
|
||||
|
||||
final class FallbackBukkitInboundHandler extends FallbackInboundHandlerAdapter {
|
||||
private static final ProtocolVersion DEFAULT_PROTOCOL_VERSION = ProtocolVersion.MINECRAFT_1_7_2;
|
||||
@@ -79,7 +78,7 @@ final class FallbackBukkitInboundHandler extends FallbackInboundHandlerAdapter {
|
||||
return;
|
||||
}
|
||||
|
||||
final int packetId = readVarInt(byteBuf);
|
||||
final int packetId = ProtocolUtil.readVarInt(byteBuf);
|
||||
final FallbackPacket packet = registry.createPacket(packetId);
|
||||
|
||||
// Skip the packet if it's not registered within Sonar's packet registry
|
||||
@@ -93,13 +92,14 @@ final class FallbackBukkitInboundHandler extends FallbackInboundHandlerAdapter {
|
||||
packet.decode(byteBuf, protocolVersion == null ? DEFAULT_PROTOCOL_VERSION : protocolVersion);
|
||||
} catch (Throwable throwable) {
|
||||
byteBuf.release();
|
||||
throw DEBUG ? new DecoderException(throwable) : QuietDecoderException.INSTANCE;
|
||||
throw ProtocolUtil.DEBUG ? new DecoderException(throwable) : QuietDecoderException.INSTANCE;
|
||||
}
|
||||
|
||||
// Check if the packet still has bytes left after we decoded it
|
||||
if (byteBuf.isReadable()) {
|
||||
byteBuf.release();
|
||||
throw DEBUG ? new DecoderException("Could not read packet to end") : QuietDecoderException.INSTANCE;
|
||||
throw ProtocolUtil.DEBUG ? new DecoderException("Could not read packet to end ("
|
||||
+ byteBuf.readableBytes() + " bytes left)") : QuietDecoderException.INSTANCE;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -122,7 +122,8 @@ final class FallbackBukkitInboundHandler extends FallbackInboundHandlerAdapter {
|
||||
updateRegistry(FallbackPacketRegistry.LOGIN, Objects.requireNonNull(protocolVersion));
|
||||
break;
|
||||
default:
|
||||
throw DEBUG ? new DecoderException("Bad intent " + handshake.getIntent()) : QuietDecoderException.INSTANCE;
|
||||
throw ProtocolUtil.DEBUG ? new DecoderException("Bad handshake intent " + handshake.getIntent())
|
||||
: QuietDecoderException.INSTANCE;
|
||||
}
|
||||
} else if (packet instanceof LoginStartPacket) {
|
||||
final LoginStartPacket loginStart = (LoginStartPacket) packet;
|
||||
|
||||
@@ -26,7 +26,7 @@ import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.api.command.SonarCommand;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import java.util.Collections;
|
||||
|
||||
public final class BungeeSonarCommand extends Command implements TabExecutor, SonarCommand {
|
||||
public BungeeSonarCommand() {
|
||||
@@ -47,9 +47,6 @@ public final class BungeeSonarCommand extends Command implements TabExecutor, So
|
||||
@Override
|
||||
public Iterable<String> onTabComplete(final @NotNull CommandSender sender, final String @NotNull [] args) {
|
||||
// Do not allow tab completion if the player does not have the required permission
|
||||
if (!sender.hasPermission("sonar.command")) {
|
||||
return emptyList();
|
||||
}
|
||||
return getCachedTabSuggestions(args);
|
||||
return sender.hasPermission("sonar.command") ? getCachedTabSuggestions(args) : Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -38,10 +38,6 @@ import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
import static java.awt.image.BufferedImage.TYPE_3BYTE_BGR;
|
||||
import static xyz.jonesdev.sonar.captcha.StandardTTFFontProvider.FONTS;
|
||||
import static xyz.jonesdev.sonar.captcha.StandardTTFFontProvider.STANDARD_FONT_SIZE;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public final class StandardCaptchaGenerator implements CaptchaGenerator {
|
||||
@@ -92,7 +88,7 @@ public final class StandardCaptchaGenerator implements CaptchaGenerator {
|
||||
|
||||
private @NotNull BufferedImage createBackgroundImage() {
|
||||
// Create a new image buffer with a 3-byte color spectrum
|
||||
final BufferedImage image = new BufferedImage(width, height, TYPE_3BYTE_BGR);
|
||||
final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
|
||||
if (background == null) {
|
||||
// Fill the entire image with a noise texture
|
||||
return FBM.filter(image, image);
|
||||
@@ -128,7 +124,7 @@ public final class StandardCaptchaGenerator implements CaptchaGenerator {
|
||||
|
||||
for (int i = 0; i < answer.length; i++) {
|
||||
// Create a glyph vector for the character with a random font
|
||||
final Font font = FONTS[RANDOM.nextInt(FONTS.length)];
|
||||
final Font font = StandardTTFFontProvider.FONTS[RANDOM.nextInt(StandardTTFFontProvider.FONTS.length)];
|
||||
glyphs[i] = font.createGlyphVector(ctx, String.valueOf(answer[i]));
|
||||
}
|
||||
|
||||
@@ -139,7 +135,7 @@ public final class StandardCaptchaGenerator implements CaptchaGenerator {
|
||||
.mapToDouble(glyph -> glyph.getLogicalBounds().getWidth() * scalingXY - 1)
|
||||
.sum();
|
||||
double beginX = Math.max(Math.min(width / 2D - totalWidth / 2D, totalWidth), 0);
|
||||
double beginY = (height + STANDARD_FONT_SIZE / 2D) / 2D + scalingXY;
|
||||
double beginY = (height + StandardTTFFontProvider.STANDARD_FONT_SIZE / 2D) / 2D + scalingXY;
|
||||
|
||||
// Draw each glyph one by one
|
||||
for (final GlyphVector glyph : glyphs) {
|
||||
|
||||
@@ -46,7 +46,6 @@ import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
@@ -142,7 +141,7 @@ public abstract class SonarBootstrap<T> implements Sonar {
|
||||
|
||||
// Update ratelimiter cache
|
||||
getFallback().setRatelimiter(getConfig().getVerification().getReconnectDelay() > 0L
|
||||
? new CaffeineCacheRatelimiter(getConfig().getVerification().getReconnectDelay(), TimeUnit.MILLISECONDS)
|
||||
? new CaffeineCacheRatelimiter(Duration.ofMillis(getConfig().getVerification().getReconnectDelay()))
|
||||
: NoopCacheRatelimiter.INSTANCE);
|
||||
|
||||
// Update blacklist cache
|
||||
|
||||
@@ -83,8 +83,7 @@ public final class FallbackInboundHandler extends ChannelInboundHandlerAdapter {
|
||||
* will run before the connection knows that there has been an error.
|
||||
*/
|
||||
@Override
|
||||
public void exceptionCaught(final @NotNull ChannelHandlerContext ctx,
|
||||
final @NotNull Throwable cause) throws Exception {
|
||||
public void exceptionCaught(final @NotNull ChannelHandlerContext ctx, final Throwable cause) throws Exception {
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
+19
-18
@@ -25,10 +25,13 @@ import org.jetbrains.annotations.Nullable;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.api.fingerprint.FingerprintingUtil;
|
||||
import xyz.jonesdev.sonar.common.fallback.netty.FallbackTimeoutHandler;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketEncoder;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketRegistry;
|
||||
import xyz.jonesdev.sonar.common.statistics.GlobalSonarStatistics;
|
||||
import xyz.jonesdev.sonar.common.util.GeyserUtil;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
import xyz.jonesdev.sonar.common.util.exception.QuietDecoderException;
|
||||
|
||||
import java.net.InetAddress;
|
||||
@@ -38,7 +41,6 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.FallbackPipelines.*;
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.FallbackPreparer.*;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.closeWith;
|
||||
|
||||
@AllArgsConstructor
|
||||
@RequiredArgsConstructor
|
||||
@@ -72,17 +74,23 @@ public abstract class FallbackInboundHandlerAdapter extends ChannelInboundHandle
|
||||
final @NotNull Runnable initialLoginAction,
|
||||
final @NotNull String username,
|
||||
final @NotNull InetSocketAddress socketAddress) throws Exception {
|
||||
// Check if the player has already sent a login packet
|
||||
if (this.username != null) {
|
||||
// Count every single attempted login
|
||||
GlobalSonarStatistics.countLogin();
|
||||
// Ensure that the player sent a handshake packet
|
||||
if (protocolVersion == null) {
|
||||
throw QuietDecoderException.INSTANCE;
|
||||
}
|
||||
// Connections from unknown protocol versions will be discarded
|
||||
// as this is the safest way of handling unwanted connections.
|
||||
// Sonar does not support snapshots or Minecraft versions older than 1.7.2
|
||||
if (protocolVersion.isUnknown()) {
|
||||
customDisconnect(ctx.channel(), unsupportedVersion, ProtocolVersion.MINECRAFT_1_7_2);
|
||||
return;
|
||||
}
|
||||
// Ensure that the player has not sent a login packet yet
|
||||
if (this.username != null) {
|
||||
throw QuietDecoderException.INSTANCE;
|
||||
}
|
||||
GlobalSonarStatistics.countLogin();
|
||||
this.username = username;
|
||||
|
||||
final InetAddress inetAddress = socketAddress.getAddress();
|
||||
@@ -149,17 +157,8 @@ public abstract class FallbackInboundHandlerAdapter extends ChannelInboundHandle
|
||||
return null;
|
||||
}
|
||||
|
||||
return () -> {
|
||||
// Check if the username matches the valid name regex to prevent
|
||||
// UTF-16 names or other types of exploits
|
||||
if (!Sonar.get().getConfig().getVerification().getValidNameRegex().matcher(username).matches()) {
|
||||
customDisconnect(ctx.channel(), invalidUsername, protocolVersion);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create an instance for the user and let the verification handler take over the channel
|
||||
new FallbackUserWrapper(ctx.channel(), inetAddress, protocolVersion, username, fingerprint, geyser);
|
||||
};
|
||||
// Create an instance for the user and let the verification handler take over the channel
|
||||
return () -> new FallbackUserWrapper(ctx, inetAddress, protocolVersion, username, fingerprint, geyser);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -214,7 +213,7 @@ public abstract class FallbackInboundHandlerAdapter extends ChannelInboundHandle
|
||||
|
||||
private static void customDisconnect(final @NotNull Channel channel,
|
||||
final @NotNull FallbackPacket packet,
|
||||
final ProtocolVersion protocolVersion) {
|
||||
final @NotNull ProtocolVersion protocolVersion) {
|
||||
if (channel.isActive()) {
|
||||
if (channel.eventLoop().inEventLoop()) {
|
||||
_customDisconnect(channel, packet, protocolVersion);
|
||||
@@ -229,7 +228,8 @@ public abstract class FallbackInboundHandlerAdapter extends ChannelInboundHandle
|
||||
*/
|
||||
private static void _customDisconnect(final @NotNull Channel channel,
|
||||
final @NotNull FallbackPacket packet,
|
||||
final ProtocolVersion protocolVersion) {
|
||||
final @NotNull ProtocolVersion protocolVersion) {
|
||||
// TODO: recode this
|
||||
// Remove the connection handler pipeline to completely take over the channel
|
||||
final String handler = Sonar.get().getPlatform().getConnectionHandler();
|
||||
if (channel.pipeline().context(handler) != null) {
|
||||
@@ -242,9 +242,10 @@ public abstract class FallbackInboundHandlerAdapter extends ChannelInboundHandle
|
||||
// We don't need to update the encoder if it has already been replaced by Sonar
|
||||
if (!(currentEncoder instanceof FallbackPacketEncoder)) {
|
||||
final FallbackPacketEncoder newEncoder = new FallbackPacketEncoder(protocolVersion);
|
||||
newEncoder.updateRegistry(FallbackPacketRegistry.LOGIN);
|
||||
channel.pipeline().replace(encoder, FALLBACK_PACKET_ENCODER, newEncoder);
|
||||
}
|
||||
closeWith(channel, protocolVersion, packet);
|
||||
ProtocolUtil.closeWith(channel, protocolVersion, packet);
|
||||
} else {
|
||||
channel.close();
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package xyz.jonesdev.sonar.common.fallback;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
@@ -34,18 +35,16 @@ import xyz.jonesdev.sonar.common.fallback.netty.FallbackVarInt21FrameDecoder;
|
||||
import xyz.jonesdev.sonar.common.fallback.netty.FallbackVarIntLengthEncoder;
|
||||
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.FallbackPreparer;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.play.DisconnectPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.verification.FallbackPreJoinHandler;
|
||||
import xyz.jonesdev.sonar.common.statistics.GlobalSonarStatistics;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
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;
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.FallbackPreparer.loginSuccess;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.closeWith;
|
||||
|
||||
@Getter
|
||||
@ToString(of = {"protocolVersion", "inetAddress", "geyser"})
|
||||
@@ -61,13 +60,13 @@ public final class FallbackUserWrapper implements FallbackUser {
|
||||
@Setter
|
||||
private boolean forceCaptcha;
|
||||
|
||||
public FallbackUserWrapper(final @NotNull Channel channel,
|
||||
public FallbackUserWrapper(final @NotNull ChannelHandlerContext ctx,
|
||||
final @NotNull InetAddress inetAddress,
|
||||
final @NotNull ProtocolVersion protocolVersion,
|
||||
final @NotNull String username,
|
||||
final @NotNull String fingerprint,
|
||||
final boolean geyser) {
|
||||
this.channel = channel;
|
||||
this.channel = ctx.channel();
|
||||
this.inetAddress = inetAddress;
|
||||
this.protocolVersion = protocolVersion;
|
||||
this.username = username;
|
||||
@@ -88,7 +87,7 @@ public final class FallbackUserWrapper implements FallbackUser {
|
||||
}
|
||||
|
||||
// Call the VerifyJoinEvent for external API usage
|
||||
Sonar.get().getEventManager().publish(new UserVerifyJoinEvent(username, this));
|
||||
Sonar.get().getEventManager().publish(new UserVerifyJoinEvent(this));
|
||||
|
||||
// Run this in the channel's event loop to avoid issues
|
||||
channel.eventLoop().execute(() -> {
|
||||
@@ -101,24 +100,34 @@ public final class FallbackUserWrapper implements FallbackUser {
|
||||
Sonar.get().getFallback().getConnected().compute(inetAddress, (__, v) -> true);
|
||||
|
||||
// Replace normal encoder to allow custom packets
|
||||
// TODO: recode injection
|
||||
final FallbackPacketEncoder newEncoder = new FallbackPacketEncoder(protocolVersion);
|
||||
channel.pipeline().addFirst(FALLBACK_FRAME_ENCODER, FallbackVarIntLengthEncoder.INSTANCE);
|
||||
channel.pipeline().addLast(FALLBACK_PACKET_ENCODER, newEncoder);
|
||||
|
||||
// Send LoginSuccess packet to make the client think they are joining the server
|
||||
write(loginSuccess);
|
||||
|
||||
// The LoginSuccess packet has been sent, now we can change the registry state
|
||||
newEncoder.updateRegistry(protocolVersion.compareTo(MINECRAFT_1_20_2) >= 0 ? CONFIG : GAME);
|
||||
|
||||
// Replace normal decoder to allow custom packets
|
||||
// TODO: recode injection
|
||||
final FallbackPacketDecoder newDecoder = new FallbackPacketDecoder(protocolVersion);
|
||||
channel.pipeline().addFirst(FALLBACK_FRAME_DECODER, new FallbackVarInt21FrameDecoder());
|
||||
channel.pipeline().addLast(FALLBACK_PACKET_DECODER, newDecoder);
|
||||
|
||||
// We're sending the LoginSuccess packet now
|
||||
newDecoder.updateRegistry(FallbackPacketRegistry.LOGIN);
|
||||
newEncoder.updateRegistry(FallbackPacketRegistry.LOGIN);
|
||||
// Send LoginSuccess packet to make the client think they are joining the server
|
||||
write(FallbackPreparer.loginSuccess);
|
||||
|
||||
// pre-1.20.2 clients do not have the configuration stage
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
|
||||
newDecoder.updateRegistry(FallbackPacketRegistry.GAME);
|
||||
newEncoder.updateRegistry(FallbackPacketRegistry.GAME);
|
||||
}
|
||||
|
||||
// Listen for all incoming packets by setting the packet listener
|
||||
newDecoder.setListener(new FallbackPreJoinHandler(this));
|
||||
|
||||
// Make sure to catch all exceptions during the verification
|
||||
// TODO: recode injection
|
||||
channel.pipeline().addLast(FALLBACK_TAIL_EXCEPTIONS, FallbackTailExceptionsHandler.INSTANCE);
|
||||
});
|
||||
}
|
||||
@@ -126,7 +135,7 @@ public final class FallbackUserWrapper implements FallbackUser {
|
||||
@Override
|
||||
public void disconnect(final @NotNull Component reason) {
|
||||
final FallbackPacketEncoder encoder = channel.pipeline().get(FallbackPacketEncoder.class);
|
||||
final boolean duringLogin = encoder != null && encoder.getPacketRegistry() != GAME;
|
||||
closeWith(channel, protocolVersion, DisconnectPacket.create(reason, duringLogin));
|
||||
final boolean duringLogin = encoder != null && encoder.getPacketRegistry() == FallbackPacketRegistry.LOGIN;
|
||||
ProtocolUtil.closeWith(channel, protocolVersion, new DisconnectPacket(reason, duringLogin));
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -30,8 +30,7 @@ public final class FallbackTailExceptionsHandler extends ChannelDuplexHandler {
|
||||
// does not have any other pipelines that could handle this error.
|
||||
// Additionally, this will also run after our custom decoder.
|
||||
@Override
|
||||
public void exceptionCaught(final @NotNull ChannelHandlerContext ctx,
|
||||
final @NotNull Throwable cause) throws Exception {
|
||||
public void exceptionCaught(final @NotNull ChannelHandlerContext ctx, final Throwable cause) throws Exception {
|
||||
// Close the channel if we encounter any errors.
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
+3
-3
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback;
|
||||
package xyz.jonesdev.sonar.common.fallback.netty;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
@@ -26,12 +26,12 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
public final class FallbackTimeoutHandler extends IdleStateHandler {
|
||||
public FallbackTimeoutHandler(final int readTimeout, final int writeTimeout, final TimeUnit timeUnit) {
|
||||
super(readTimeout, writeTimeout, 0L, timeUnit);
|
||||
super(false, readTimeout, writeTimeout, 0L, timeUnit);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelIdle(final @NotNull ChannelHandlerContext ctx,
|
||||
final @NotNull IdleStateEvent idleStateEvent) throws Exception {
|
||||
final IdleStateEvent idleStateEvent) throws Exception {
|
||||
// The netty (default) ReadTimeoutHandler would normally just throw an Exception
|
||||
// The default ReadTimeoutHandler does only check for the boolean 'closed' and
|
||||
// still throws the Exception even if the channel is closed
|
||||
+6
-6
@@ -21,14 +21,13 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import io.netty.util.ByteProcessor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
import xyz.jonesdev.sonar.common.util.exception.QuietDecoderException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static io.netty.util.ByteProcessor.FIND_NON_NUL;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.DEBUG;
|
||||
|
||||
// https://github.com/PaperMC/Velocity/blob/dev/3.0.0/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java
|
||||
public final class FallbackVarInt21FrameDecoder extends ByteToMessageDecoder {
|
||||
|
||||
@@ -42,8 +41,9 @@ public final class FallbackVarInt21FrameDecoder extends ByteToMessageDecoder {
|
||||
}
|
||||
|
||||
// Skip any runs of 0x00 we might find
|
||||
final int packetStart = byteBuf.forEachByte(FIND_NON_NUL);
|
||||
final int packetStart = byteBuf.forEachByte(ByteProcessor.FIND_NON_NUL);
|
||||
if (packetStart == -1) {
|
||||
byteBuf.clear();
|
||||
return;
|
||||
}
|
||||
byteBuf.readerIndex(packetStart);
|
||||
@@ -56,7 +56,7 @@ public final class FallbackVarInt21FrameDecoder extends ByteToMessageDecoder {
|
||||
return;
|
||||
}
|
||||
if (length <= 0) {
|
||||
throw DEBUG ? new DecoderException("Bad VarInt length: " + length) : QuietDecoderException.INSTANCE;
|
||||
throw ProtocolUtil.DEBUG ? new DecoderException("Bad VarInt length: " + length) : QuietDecoderException.INSTANCE;
|
||||
}
|
||||
|
||||
if (byteBuf.readableBytes() < length) {
|
||||
@@ -75,7 +75,7 @@ public final class FallbackVarInt21FrameDecoder extends ByteToMessageDecoder {
|
||||
final int wholeOrMore = byteBuf.getIntLE(byteBuf.readerIndex());
|
||||
final int atStop = ~wholeOrMore & 0x808080;
|
||||
if (atStop == 0) {
|
||||
throw DEBUG ? new DecoderException("VarInt too big") : QuietDecoderException.INSTANCE;
|
||||
throw ProtocolUtil.DEBUG ? new DecoderException("VarInt too big") : QuietDecoderException.INSTANCE;
|
||||
}
|
||||
|
||||
final int bitsToKeep = Integer.numberOfTrailingZeros(atStop) + 1;
|
||||
|
||||
+5
-7
@@ -22,12 +22,10 @@ import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.varIntBytes;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
|
||||
|
||||
// https://github.com/PaperMC/Velocity/blob/dev/3.0.0/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintLengthEncoder.java
|
||||
@ChannelHandler.Sharable
|
||||
public final class FallbackVarIntLengthEncoder extends MessageToMessageEncoder<ByteBuf> {
|
||||
@@ -37,12 +35,12 @@ public final class FallbackVarIntLengthEncoder extends MessageToMessageEncoder<B
|
||||
protected void encode(final @NotNull ChannelHandlerContext ctx,
|
||||
final @NotNull ByteBuf byteBuf,
|
||||
final @NotNull List<Object> out) throws Exception {
|
||||
final int length = byteBuf.readableBytes();
|
||||
final int varIntLength = varIntBytes(length);
|
||||
final int readableBytes = byteBuf.readableBytes();
|
||||
final int length = ProtocolUtil.varIntBytes(readableBytes);
|
||||
|
||||
final ByteBuf lenBuf = ctx.alloc().buffer(varIntLength);
|
||||
final ByteBuf lenBuf = ctx.alloc().buffer(length);
|
||||
|
||||
writeVarInt(lenBuf, length);
|
||||
ProtocolUtil.writeVarInt(lenBuf, readableBytes);
|
||||
out.add(lenBuf);
|
||||
out.add(byteBuf.retain());
|
||||
}
|
||||
|
||||
+3
-1
@@ -15,12 +15,14 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.captcha;
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.event.impl.CaptchaGenerationEndEvent;
|
||||
import xyz.jonesdev.sonar.api.timer.SystemTimer;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.map.MapCaptchaInfo;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.map.MapColorPalette;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.Random;
|
||||
+2
-4
@@ -39,20 +39,18 @@ public interface FallbackPacket {
|
||||
void decode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) throws Exception;
|
||||
|
||||
/**
|
||||
* @param byteBuf ByteBuf
|
||||
* @param protocolVersion Protocol version of the player
|
||||
* @return The minimum allowed length of the decoded packet
|
||||
*/
|
||||
default int expectedMinLength(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
default int expectedMinLength(final ProtocolVersion protocolVersion) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param byteBuf ByteBuf
|
||||
* @param protocolVersion Protocol version of the player
|
||||
* @return The maximum allowed length of the decoded packet
|
||||
*/
|
||||
default int expectedMaxLength(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
default int expectedMaxLength(final ProtocolVersion protocolVersion) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
+18
-26
@@ -21,19 +21,16 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
import xyz.jonesdev.sonar.common.util.exception.QuietDecoderException;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_20_2;
|
||||
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.fallback.protocol.FallbackPreparer.maxTotalPacketsSent;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.DEBUG;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.readVarInt;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class FallbackPacketDecoder extends ChannelInboundHandlerAdapter {
|
||||
private final ProtocolVersion protocolVersion;
|
||||
private FallbackPacketRegistry.ProtocolRegistry registry;
|
||||
@@ -41,18 +38,12 @@ public final class FallbackPacketDecoder extends ChannelInboundHandlerAdapter {
|
||||
private FallbackPacketListener listener;
|
||||
private int totalPacketsSent;
|
||||
|
||||
public FallbackPacketDecoder(final @NotNull ProtocolVersion protocolVersion) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
updateRegistry(protocolVersion.compareTo(MINECRAFT_1_20_2) >= 0 ? LOGIN : GAME);
|
||||
}
|
||||
|
||||
public void updateRegistry(final @NotNull FallbackPacketRegistry registry) {
|
||||
this.registry = registry.getProtocolRegistry(SERVERBOUND, protocolVersion);
|
||||
this.registry = registry.getProtocolRegistry(FallbackPacketRegistry.Direction.SERVERBOUND, protocolVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(final @NotNull ChannelHandlerContext ctx,
|
||||
final @NotNull Object msg) throws Exception {
|
||||
public void channelRead(final @NotNull ChannelHandlerContext ctx, final Object msg) throws Exception {
|
||||
if (msg instanceof ByteBuf) {
|
||||
final ByteBuf byteBuf = (ByteBuf) msg;
|
||||
|
||||
@@ -66,11 +57,11 @@ public final class FallbackPacketDecoder extends ChannelInboundHandlerAdapter {
|
||||
|
||||
// Don't allow the player to spam packets to overload netty
|
||||
if (++totalPacketsSent > maxTotalPacketsSent) {
|
||||
throw DEBUG ? new DecoderException("Sent too many packets") : QuietDecoderException.INSTANCE;
|
||||
throw ProtocolUtil.DEBUG ? new DecoderException("Sent too many packets") : QuietDecoderException.INSTANCE;
|
||||
}
|
||||
|
||||
// Read the packet ID and then create the packet from it
|
||||
final int packetId = readVarInt(byteBuf);
|
||||
final int packetId = ProtocolUtil.readVarInt(byteBuf);
|
||||
final FallbackPacket packet = registry.createPacket(packetId);
|
||||
|
||||
// Skip the packet if it's not registered within Sonar's packet registry
|
||||
@@ -79,18 +70,19 @@ public final class FallbackPacketDecoder extends ChannelInboundHandlerAdapter {
|
||||
}
|
||||
|
||||
// Ensure that the packet isn't too large or too small
|
||||
checkPacketSize(byteBuf, packet);
|
||||
checkPacketSize(byteBuf.readableBytes(), packet);
|
||||
|
||||
try {
|
||||
// Try to decode the packet for the given protocol version
|
||||
packet.decode(byteBuf, protocolVersion);
|
||||
} catch (Throwable throwable) {
|
||||
throw DEBUG ? new DecoderException(throwable) : QuietDecoderException.INSTANCE;
|
||||
throw ProtocolUtil.DEBUG ? new DecoderException(throwable) : QuietDecoderException.INSTANCE;
|
||||
}
|
||||
|
||||
// Check if the packet still has bytes left after we decoded it
|
||||
if (byteBuf.isReadable()) {
|
||||
throw DEBUG ? new DecoderException("Could not read packet to end") : QuietDecoderException.INSTANCE;
|
||||
throw ProtocolUtil.DEBUG ? new DecoderException("Could not read packet to end ("
|
||||
+ byteBuf.readableBytes() + " bytes left)"): QuietDecoderException.INSTANCE;
|
||||
}
|
||||
|
||||
// Let our verification handler process the packet
|
||||
@@ -106,16 +98,16 @@ public final class FallbackPacketDecoder extends ChannelInboundHandlerAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkPacketSize(final @NotNull ByteBuf byteBuf,
|
||||
final @NotNull FallbackPacket packet) throws Exception {
|
||||
final int packetSize = byteBuf.readableBytes();
|
||||
final int expectedMaxLen = packet.expectedMaxLength(byteBuf, protocolVersion);
|
||||
private void checkPacketSize(final int packetSize, final @NotNull FallbackPacket packet) throws Exception {
|
||||
final int expectedMaxLen = packet.expectedMaxLength(protocolVersion);
|
||||
if (expectedMaxLen != -1 && packetSize > expectedMaxLen) {
|
||||
throw DEBUG ? new DecoderException("Packet too large: " + packetSize) : QuietDecoderException.INSTANCE;
|
||||
throw ProtocolUtil.DEBUG ? new DecoderException("Packet too large: " + packetSize + " max: " + expectedMaxLen)
|
||||
: QuietDecoderException.INSTANCE;
|
||||
}
|
||||
final int expectedMinLen = packet.expectedMinLength(byteBuf, protocolVersion);
|
||||
final int expectedMinLen = packet.expectedMinLength(protocolVersion);
|
||||
if (expectedMinLen != -1 && packetSize < expectedMinLen) {
|
||||
throw DEBUG ? new DecoderException("Packet too small: " + packetSize) : QuietDecoderException.INSTANCE;
|
||||
throw ProtocolUtil.DEBUG ? new DecoderException("Packet too small: " + packetSize + " min: " + expectedMinLen)
|
||||
: QuietDecoderException.INSTANCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-15
@@ -21,37 +21,31 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
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.util.ProtocolUtil.writeVarInt;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class FallbackPacketEncoder extends MessageToByteEncoder<FallbackPacket> {
|
||||
private final ProtocolVersion protocolVersion;
|
||||
@Getter
|
||||
private FallbackPacketRegistry packetRegistry;
|
||||
private FallbackPacketRegistry.ProtocolRegistry protocolRegistry;
|
||||
|
||||
public FallbackPacketEncoder(final ProtocolVersion protocolVersion) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
updateRegistry(LOGIN);
|
||||
}
|
||||
|
||||
public void updateRegistry(final @NotNull FallbackPacketRegistry packetRegistry) {
|
||||
this.packetRegistry = packetRegistry;
|
||||
this.protocolRegistry = packetRegistry.getProtocolRegistry(CLIENTBOUND, protocolVersion);
|
||||
this.protocolRegistry = packetRegistry.getProtocolRegistry(FallbackPacketRegistry.Direction.CLIENTBOUND, protocolVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(final ChannelHandlerContext ctx,
|
||||
final @NotNull FallbackPacket packet,
|
||||
final ByteBuf out) throws Exception {
|
||||
final FallbackPacket originalPacket = packet instanceof FallbackPacketSnapshot
|
||||
? ((FallbackPacketSnapshot) packet).getOriginalPacket() : packet;
|
||||
final int packetId = protocolRegistry.getPacketId(originalPacket.getClass());
|
||||
writeVarInt(out, packetId);
|
||||
final @NotNull ByteBuf out) throws Exception {
|
||||
final Class<? extends FallbackPacket> originalPacket = packet instanceof FallbackPacketSnapshot
|
||||
? ((FallbackPacketSnapshot) packet).getOriginalPacketClass() : packet.getClass();
|
||||
final int packetId = protocolRegistry.getPacketId(originalPacket);
|
||||
ProtocolUtil.writeVarInt(out, packetId);
|
||||
packet.encode(out, protocolVersion);
|
||||
}
|
||||
}
|
||||
+84
-32
@@ -105,7 +105,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x24, MINECRAFT_1_19_3, true),
|
||||
map(0x28, MINECRAFT_1_19_4, true),
|
||||
map(0x29, MINECRAFT_1_20_2, true),
|
||||
map(0x2B, MINECRAFT_1_20_5, true));
|
||||
map(0x2B, MINECRAFT_1_20_5, true),
|
||||
map(0x2C, MINECRAFT_1_21_2, true));
|
||||
clientbound.register(KeepAlivePacket.class, KeepAlivePacket::new,
|
||||
map(0x00, MINECRAFT_1_7_2, true),
|
||||
map(0x1F, MINECRAFT_1_9, true),
|
||||
@@ -120,7 +121,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x1F, MINECRAFT_1_19_3, true),
|
||||
map(0x23, MINECRAFT_1_19_4, true),
|
||||
map(0x24, MINECRAFT_1_20_2, true),
|
||||
map(0x26, MINECRAFT_1_20_5, true));
|
||||
map(0x26, MINECRAFT_1_20_5, true),
|
||||
map(0x27, MINECRAFT_1_21_2, true));
|
||||
clientbound.register(DisconnectPacket.class, DisconnectPacket::new,
|
||||
map(0x40, MINECRAFT_1_7_2, true),
|
||||
map(0x1A, MINECRAFT_1_9, true),
|
||||
@@ -151,7 +153,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x38, MINECRAFT_1_19_3, true),
|
||||
map(0x3C, MINECRAFT_1_19_4, true),
|
||||
map(0x3E, MINECRAFT_1_20_2, true),
|
||||
map(0x40, MINECRAFT_1_20_5, true));
|
||||
map(0x40, MINECRAFT_1_20_5, true),
|
||||
map(0x42, MINECRAFT_1_21_2, true));
|
||||
clientbound.register(PlayerAbilitiesPacket.class, PlayerAbilitiesPacket::new,
|
||||
map(0x39, MINECRAFT_1_7_2, true),
|
||||
map(0x2B, MINECRAFT_1_9, true),
|
||||
@@ -167,7 +170,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x30, MINECRAFT_1_19_3, true),
|
||||
map(0x34, MINECRAFT_1_19_4, true),
|
||||
map(0x36, MINECRAFT_1_20_2, true),
|
||||
map(0x38, MINECRAFT_1_20_5, true));
|
||||
map(0x38, MINECRAFT_1_20_5, true),
|
||||
map(0x3A, MINECRAFT_1_21_2, true));
|
||||
clientbound.register(ChunkDataPacket.class, ChunkDataPacket::new,
|
||||
map(0x21, MINECRAFT_1_7_2, true),
|
||||
map(0x20, MINECRAFT_1_9, true),
|
||||
@@ -182,7 +186,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x20, MINECRAFT_1_19_3, true),
|
||||
map(0x24, MINECRAFT_1_19_4, true),
|
||||
map(0x25, MINECRAFT_1_20_2, true),
|
||||
map(0x27, MINECRAFT_1_20_5, true));
|
||||
map(0x27, MINECRAFT_1_20_5, true),
|
||||
map(0x28, MINECRAFT_1_21_2, true));
|
||||
clientbound.register(RemoveEntitiesPacket.class, RemoveEntitiesPacket::new,
|
||||
map(0x13, MINECRAFT_1_7_2, true),
|
||||
map(0x30, MINECRAFT_1_9, true),
|
||||
@@ -200,7 +205,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x3E, MINECRAFT_1_19_4, true),
|
||||
map(0x3E, MINECRAFT_1_19_4, true),
|
||||
map(0x40, MINECRAFT_1_20_2, true),
|
||||
map(0x42, MINECRAFT_1_20_5, true));
|
||||
map(0x42, MINECRAFT_1_20_5, true),
|
||||
map(0x47, MINECRAFT_1_21_2, true));
|
||||
clientbound.register(SetPassengersPacket.class, SetPassengersPacket::new,
|
||||
map(0x1B, MINECRAFT_1_7_2, true),
|
||||
map(0x40, MINECRAFT_1_9, true),
|
||||
@@ -215,7 +221,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x59, MINECRAFT_1_19_4, true),
|
||||
map(0x5B, MINECRAFT_1_20_2, true),
|
||||
map(0x5D, MINECRAFT_1_20_3, true),
|
||||
map(0x5F, MINECRAFT_1_20_5, true));
|
||||
map(0x5F, MINECRAFT_1_20_5, true),
|
||||
map(0x65, MINECRAFT_1_21_2, true));
|
||||
clientbound.register(VehicleMovePacket.class, VehicleMovePacket::new,
|
||||
map(0x29, MINECRAFT_1_9, true),
|
||||
map(0x2B, MINECRAFT_1_13, true),
|
||||
@@ -229,7 +236,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x2A, MINECRAFT_1_19_3, true),
|
||||
map(0x2E, MINECRAFT_1_19_4, true),
|
||||
map(0x2F, MINECRAFT_1_20_2, true),
|
||||
map(0x31, MINECRAFT_1_20_5, true));
|
||||
map(0x31, MINECRAFT_1_20_5, true),
|
||||
map(0x33, MINECRAFT_1_21_2, true));
|
||||
clientbound.register(SpawnEntityPacket.class, SpawnEntityPacket::new,
|
||||
map(0x0E, MINECRAFT_1_7_2, true),
|
||||
map(0x00, MINECRAFT_1_9, true),
|
||||
@@ -248,7 +256,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x43, MINECRAFT_1_19_4, true),
|
||||
map(0x45, MINECRAFT_1_20_2, true),
|
||||
map(0x47, MINECRAFT_1_20_3, true),
|
||||
map(0x49, MINECRAFT_1_20_5, true));
|
||||
map(0x49, MINECRAFT_1_20_5, true),
|
||||
map(0x4E, MINECRAFT_1_21_2, true));
|
||||
clientbound.register(TransactionPacket.class, TransactionPacket::new,
|
||||
map(0x32, MINECRAFT_1_7_2, true),
|
||||
map(0x11, MINECRAFT_1_9, true),
|
||||
@@ -262,7 +271,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x2E, MINECRAFT_1_19_3, true),
|
||||
map(0x32, MINECRAFT_1_19_4, true),
|
||||
map(0x33, MINECRAFT_1_20_2, true),
|
||||
map(0x35, MINECRAFT_1_20_5, true));
|
||||
map(0x35, MINECRAFT_1_20_5, true),
|
||||
map(0x37, MINECRAFT_1_21_2, true));
|
||||
clientbound.register(SetDefaultSpawnPositionPacket.class, SetDefaultSpawnPositionPacket::new,
|
||||
map(0x05, MINECRAFT_1_7_2, true),
|
||||
map(0x43, MINECRAFT_1_9, true),
|
||||
@@ -279,7 +289,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x50, MINECRAFT_1_19_4, true),
|
||||
map(0x52, MINECRAFT_1_20_2, true),
|
||||
map(0x54, MINECRAFT_1_20_3, true),
|
||||
map(0x56, MINECRAFT_1_20_5, true));
|
||||
map(0x56, MINECRAFT_1_20_5, true),
|
||||
map(0x5B, MINECRAFT_1_21_2, true));
|
||||
clientbound.register(MapDataPacket.class, MapDataPacket::new,
|
||||
map(0x34, MINECRAFT_1_7_2, true),
|
||||
map(0x24, MINECRAFT_1_9, true),
|
||||
@@ -293,7 +304,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x25, MINECRAFT_1_19_3, true),
|
||||
map(0x29, MINECRAFT_1_19_4, true),
|
||||
map(0x2A, MINECRAFT_1_20_2, true),
|
||||
map(0x2C, MINECRAFT_1_20_5, true));
|
||||
map(0x2C, MINECRAFT_1_20_5, true),
|
||||
map(0x2D, MINECRAFT_1_21_2, true));
|
||||
clientbound.register(SetContainerSlotPacket.class, SetContainerSlotPacket::new,
|
||||
map(0x2F, MINECRAFT_1_7_2, true),
|
||||
map(0x16, MINECRAFT_1_9, true),
|
||||
@@ -321,7 +333,8 @@ public enum FallbackPacketRegistry {
|
||||
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));
|
||||
map(0x5C, MINECRAFT_1_20_5, true),
|
||||
map(0x61, MINECRAFT_1_21_2, true));
|
||||
clientbound.register(SystemChatPacket.class, SystemChatPacket::new,
|
||||
map(0x02, MINECRAFT_1_7_2, true),
|
||||
map(0x0F, MINECRAFT_1_9, true),
|
||||
@@ -335,12 +348,15 @@ public enum FallbackPacketRegistry {
|
||||
map(0x64, MINECRAFT_1_19_4, true),
|
||||
map(0x67, MINECRAFT_1_20_2, true),
|
||||
map(0x69, MINECRAFT_1_20_3, true),
|
||||
map(0x6C, MINECRAFT_1_20_5, true));
|
||||
map(0x6C, MINECRAFT_1_20_5, true),
|
||||
map(0x73, MINECRAFT_1_21_2, true));
|
||||
clientbound.register(GameEventPacket.class, GameEventPacket::new,
|
||||
map(0x20, MINECRAFT_1_20_3, true),
|
||||
map(0x22, MINECRAFT_1_20_5, true));
|
||||
map(0x22, MINECRAFT_1_20_5, true),
|
||||
map(0x23, MINECRAFT_1_21_2, true));
|
||||
clientbound.register(TransferPacket.class, TransferPacket::new,
|
||||
map(0x73, MINECRAFT_1_20_5, true));
|
||||
map(0x73, MINECRAFT_1_20_5, true),
|
||||
map(0x7A, MINECRAFT_1_21_2, true));
|
||||
clientbound.register(SetHeldItemPacket.class, SetHeldItemPacket::new,
|
||||
map(0x09, MINECRAFT_1_7_2, true),
|
||||
map(0x37, MINECRAFT_1_9, true),
|
||||
@@ -357,7 +373,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x4D, MINECRAFT_1_19_4, true),
|
||||
map(0x4F, MINECRAFT_1_20_2, true),
|
||||
map(0x51, MINECRAFT_1_20_3, true),
|
||||
map(0x53, MINECRAFT_1_20_5, true));
|
||||
map(0x53, MINECRAFT_1_20_5, true),
|
||||
map(0x63, MINECRAFT_1_21_2, true));
|
||||
clientbound.register(EntityAnimationPacket.class, EntityAnimationPacket::new,
|
||||
map(0x0B, MINECRAFT_1_7_2, true),
|
||||
map(0x06, MINECRAFT_1_9, true),
|
||||
@@ -384,8 +401,29 @@ public enum FallbackPacketRegistry {
|
||||
map(0x68, MINECRAFT_1_19_4, true),
|
||||
map(0x6B, MINECRAFT_1_20_2, true),
|
||||
map(0x6D, MINECRAFT_1_20_3, true),
|
||||
map(0x70, MINECRAFT_1_20_5, true));
|
||||
map(0x70, MINECRAFT_1_20_5, true),
|
||||
map(0x77, MINECRAFT_1_21_2, true));
|
||||
clientbound.register(UpdateTimePacket.class, UpdateTimePacket::new,
|
||||
map(0x03, MINECRAFT_1_7_2, true),
|
||||
map(0x44, MINECRAFT_1_9, true),
|
||||
map(0x46, MINECRAFT_1_12, true),
|
||||
map(0x47, MINECRAFT_1_12_1, true),
|
||||
map(0x4A, MINECRAFT_1_13, true),
|
||||
map(0x4E, MINECRAFT_1_14, true),
|
||||
map(0x4F, MINECRAFT_1_15, true),
|
||||
map(0x4E, MINECRAFT_1_16, true),
|
||||
map(0x58, MINECRAFT_1_17, true),
|
||||
map(0x59, MINECRAFT_1_18, true),
|
||||
map(0x5C, MINECRAFT_1_19_1, true),
|
||||
map(0x5A, MINECRAFT_1_19_3, true),
|
||||
map(0x5E, MINECRAFT_1_19_4, true),
|
||||
map(0x60, MINECRAFT_1_20_2, true),
|
||||
map(0x62, MINECRAFT_1_20_3, true),
|
||||
map(0x64, MINECRAFT_1_20_5, true),
|
||||
map(0x6B, MINECRAFT_1_21_2, true));
|
||||
|
||||
serverbound.register(ClientTickEndPacket.class, ClientTickEndPacket::new,
|
||||
map(0x0B, MINECRAFT_1_21_2, false));
|
||||
serverbound.register(SetHeldItemPacket.class, SetHeldItemPacket::new,
|
||||
map(0x09, MINECRAFT_1_7_2, false),
|
||||
map(0x17, MINECRAFT_1_9, false),
|
||||
@@ -398,7 +436,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x28, MINECRAFT_1_19_1, false),
|
||||
map(0x2B, MINECRAFT_1_20_2, false),
|
||||
map(0x2C, MINECRAFT_1_20_3, false),
|
||||
map(0x2F, MINECRAFT_1_20_5, false));
|
||||
map(0x2F, MINECRAFT_1_20_5, false),
|
||||
map(0x31, MINECRAFT_1_21_2, false));
|
||||
serverbound.register(PaddleBoatPacket.class, PaddleBoatPacket::new,
|
||||
map(0x11, MINECRAFT_1_9, false),
|
||||
map(0x12, MINECRAFT_1_12, false),
|
||||
@@ -413,7 +452,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x19, MINECRAFT_1_19_4, false),
|
||||
map(0x1B, MINECRAFT_1_20_2, false),
|
||||
map(0x1C, MINECRAFT_1_20_3, false),
|
||||
map(0x1F, MINECRAFT_1_20_5, false));
|
||||
map(0x1F, MINECRAFT_1_20_5, false),
|
||||
map(0x21, MINECRAFT_1_21_2, false));
|
||||
serverbound.register(PlayerInputPacket.class, PlayerInputPacket::new,
|
||||
map(0x0C, MINECRAFT_1_7_2, false),
|
||||
map(0x15, MINECRAFT_1_9, false),
|
||||
@@ -428,7 +468,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x1F, MINECRAFT_1_19_4, false),
|
||||
map(0x22, MINECRAFT_1_20_2, false),
|
||||
map(0x23, MINECRAFT_1_20_3, false),
|
||||
map(0x26, MINECRAFT_1_20_5, false));
|
||||
map(0x26, MINECRAFT_1_20_5, false),
|
||||
map(0x28, MINECRAFT_1_21_2, false));
|
||||
serverbound.register(VehicleMovePacket.class, VehicleMovePacket::new,
|
||||
map(0x10, MINECRAFT_1_9, false),
|
||||
map(0x11, MINECRAFT_1_12, false),
|
||||
@@ -443,7 +484,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x18, MINECRAFT_1_19_4, false),
|
||||
map(0x1A, MINECRAFT_1_20_2, false),
|
||||
map(0x1B, MINECRAFT_1_20_3, false),
|
||||
map(0x1E, MINECRAFT_1_20_5, false));
|
||||
map(0x1E, MINECRAFT_1_20_5, false),
|
||||
map(0x20, MINECRAFT_1_21_2, false));
|
||||
serverbound.register(SystemChatPacket.class, SystemChatPacket::new,
|
||||
map(0x01, MINECRAFT_1_7_2, false),
|
||||
map(0x02, MINECRAFT_1_9, false),
|
||||
@@ -453,7 +495,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x04, MINECRAFT_1_19, false),
|
||||
map(0x05, MINECRAFT_1_19_1, false),
|
||||
map(0x05, MINECRAFT_1_19_3, false),
|
||||
map(0x06, MINECRAFT_1_20_5, false));
|
||||
map(0x06, MINECRAFT_1_20_5, false),
|
||||
map(0x07, MINECRAFT_1_21_2, false));
|
||||
serverbound.register(KeepAlivePacket.class, KeepAlivePacket::new,
|
||||
map(0x00, MINECRAFT_1_7_2, false),
|
||||
map(0x0B, MINECRAFT_1_9, false),
|
||||
@@ -469,7 +512,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x12, MINECRAFT_1_19_4, false),
|
||||
map(0x14, MINECRAFT_1_20_2, false),
|
||||
map(0x15, MINECRAFT_1_20_3, false),
|
||||
map(0x18, MINECRAFT_1_20_5, false));
|
||||
map(0x18, MINECRAFT_1_20_5, false),
|
||||
map(0x1A, MINECRAFT_1_21_2, false));
|
||||
serverbound.register(ClientInformationPacket.class, ClientInformationPacket::new,
|
||||
map(0x15, MINECRAFT_1_7_2, false),
|
||||
map(0x04, MINECRAFT_1_9, false),
|
||||
@@ -481,7 +525,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x07, MINECRAFT_1_19_3, false),
|
||||
map(0x08, MINECRAFT_1_19_4, false),
|
||||
map(0x09, MINECRAFT_1_20_2, false),
|
||||
map(0x0A, MINECRAFT_1_20_5, false));
|
||||
map(0x0A, MINECRAFT_1_20_5, false),
|
||||
map(0xC, MINECRAFT_1_21_2, false));
|
||||
serverbound.register(PluginMessagePacket.class, PluginMessagePacket::new,
|
||||
map(0x17, MINECRAFT_1_7_2, false),
|
||||
map(0x09, MINECRAFT_1_9, false),
|
||||
@@ -495,7 +540,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x0C, MINECRAFT_1_19_3, false),
|
||||
map(0x0D, MINECRAFT_1_19_4, false),
|
||||
map(0x0F, MINECRAFT_1_20_2, false),
|
||||
map(0x12, MINECRAFT_1_21, false));
|
||||
map(0x12, MINECRAFT_1_21, false),
|
||||
map(0x14, MINECRAFT_1_21_2, false));
|
||||
serverbound.register(SetPlayerOnGround.class, SetPlayerOnGround::new,
|
||||
map(0x03, MINECRAFT_1_7_2, false),
|
||||
map(0x0F, MINECRAFT_1_9, false),
|
||||
@@ -511,7 +557,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x17, MINECRAFT_1_19_4, false),
|
||||
map(0x19, MINECRAFT_1_20_2, false),
|
||||
map(0x1A, MINECRAFT_1_20_3, false),
|
||||
map(0x1D, MINECRAFT_1_20_5, false));
|
||||
map(0x1D, MINECRAFT_1_20_5, false),
|
||||
map(0x1F, MINECRAFT_1_21_2, false));
|
||||
serverbound.register(SetPlayerPositionPacket.class, SetPlayerPositionPacket::new,
|
||||
map(0x04, MINECRAFT_1_7_2, false),
|
||||
map(0x0C, MINECRAFT_1_9, false),
|
||||
@@ -527,7 +574,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x14, MINECRAFT_1_19_4, false),
|
||||
map(0x16, MINECRAFT_1_20_2, false),
|
||||
map(0x17, MINECRAFT_1_20_3, false),
|
||||
map(0x1A, MINECRAFT_1_20_5, false));
|
||||
map(0x1A, MINECRAFT_1_20_5, false),
|
||||
map(0x1C, MINECRAFT_1_21_2, false));
|
||||
serverbound.register(SetPlayerRotationPacket.class, SetPlayerRotationPacket::new,
|
||||
map(0x05, MINECRAFT_1_7_2, false),
|
||||
map(0x0E, MINECRAFT_1_9, false),
|
||||
@@ -543,7 +591,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x16, MINECRAFT_1_19_4, false),
|
||||
map(0x18, MINECRAFT_1_20_2, false),
|
||||
map(0x19, MINECRAFT_1_20_3, false),
|
||||
map(0x1C, MINECRAFT_1_20_5, false));
|
||||
map(0x1C, MINECRAFT_1_20_5, false),
|
||||
map(0x1E, MINECRAFT_1_21_2, false));
|
||||
serverbound.register(SetPlayerPositionRotationPacket.class, SetPlayerPositionRotationPacket::new,
|
||||
map(0x06, MINECRAFT_1_7_2, false),
|
||||
map(0x0D, MINECRAFT_1_9, false),
|
||||
@@ -559,7 +608,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x15, MINECRAFT_1_19_4, false),
|
||||
map(0x17, MINECRAFT_1_20_2, false),
|
||||
map(0x18, MINECRAFT_1_20_3, false),
|
||||
map(0x1B, MINECRAFT_1_20_5, false));
|
||||
map(0x1B, MINECRAFT_1_20_5, false),
|
||||
map(0x1D, MINECRAFT_1_21_2, false));
|
||||
serverbound.register(ConfirmTeleportationPacket.class, ConfirmTeleportationPacket::new,
|
||||
map(0x00, MINECRAFT_1_9, false));
|
||||
serverbound.register(TransactionPacket.class, TransactionPacket::new,
|
||||
@@ -576,7 +626,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x20, MINECRAFT_1_19_4, false),
|
||||
map(0x23, MINECRAFT_1_20_2, false),
|
||||
map(0x24, MINECRAFT_1_20_3, false),
|
||||
map(0x27, MINECRAFT_1_20_5, false));
|
||||
map(0x27, MINECRAFT_1_20_5, false),
|
||||
map(0x29, MINECRAFT_1_21_2, false));
|
||||
serverbound.register(AnimationPacket.class, AnimationPacket::new,
|
||||
map(0x0A, MINECRAFT_1_7_2, false),
|
||||
map(0x1A, MINECRAFT_1_9, false),
|
||||
@@ -589,7 +640,8 @@ public enum FallbackPacketRegistry {
|
||||
map(0x2F, MINECRAFT_1_19_1, false),
|
||||
map(0x32, MINECRAFT_1_20_2, false),
|
||||
map(0x33, MINECRAFT_1_20_3, false),
|
||||
map(0x36, MINECRAFT_1_20_5, false));
|
||||
map(0x36, MINECRAFT_1_20_5, false),
|
||||
map(0x38, MINECRAFT_1_21_2, false));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+45
-41
@@ -19,70 +19,74 @@ package xyz.jonesdev.sonar.common.fallback.protocol;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.EncoderException;
|
||||
import io.netty.util.collection.IntObjectHashMap;
|
||||
import io.netty.util.collection.IntObjectMap;
|
||||
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 xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
import xyz.jonesdev.sonar.common.util.exception.QuietDecoderException;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.ID_TO_PROTOCOL_CONSTANT;
|
||||
|
||||
// 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;
|
||||
private final IntObjectMap<Integer> mappings = new IntObjectHashMap<>(ID_TO_PROTOCOL_CONSTANT.size());
|
||||
private final IntObjectMap<byte[]> cachedBytes = new IntObjectHashMap<>(ID_TO_PROTOCOL_CONSTANT.size());
|
||||
@Getter
|
||||
private final Class<? extends FallbackPacket> originalPacketClass;
|
||||
|
||||
public FallbackPacketSnapshot(final @NotNull FallbackPacket originalPacket) {
|
||||
final Map<Integer, Integer> hashes = new HashMap<>(ID_TO_PROTOCOL_CONSTANT.size());
|
||||
this.originalPacketClass = originalPacket.getClass();
|
||||
|
||||
final IntObjectMap<Integer> hashedData = new IntObjectHashMap<>(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;
|
||||
}
|
||||
try {
|
||||
originalPacket.encode(byteBuf, protocolVersion);
|
||||
} catch (Throwable throwable) {
|
||||
Sonar.get().getLogger().error("Could not encode packet {} for version {}: {}",
|
||||
originalPacket, protocolVersion, throwable);
|
||||
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);
|
||||
final Integer protocol = protocolVersion.getProtocol();
|
||||
final Integer hash = byteBuf.hashCode();
|
||||
final Integer hashed = hashedData.getOrDefault(hash, -1);
|
||||
|
||||
if (hashed != -1) {
|
||||
mappings.put(protocol, hashed);
|
||||
} else {
|
||||
hashedData.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);
|
||||
}
|
||||
} finally {
|
||||
// Make sure to release the buffer to avoid memory leaks
|
||||
byteBuf.release();
|
||||
}
|
||||
// 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!");
|
||||
public void encode(final @NotNull ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
final Integer hash = mappings.get(protocolVersion.getProtocol());
|
||||
if (hash == null) {
|
||||
throw ProtocolUtil.DEBUG ? new EncoderException("Unable to find hash") : QuietDecoderException.INSTANCE;
|
||||
}
|
||||
final byte[] bytes = cachedBytes.get(hash);
|
||||
if (bytes == null) {
|
||||
throw ProtocolUtil.DEBUG ? new EncoderException("Unable to find cached packet") : QuietDecoderException.INSTANCE;
|
||||
}
|
||||
byteBuf.writeBytes(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+80
-50
@@ -22,15 +22,18 @@ import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||
import net.kyori.adventure.nbt.IntBinaryTag;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.config.SonarConfiguration;
|
||||
import xyz.jonesdev.sonar.api.event.impl.CaptchaGenerationStartEvent;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.captcha.StandardCaptchaGenerator;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.block.BlockType;
|
||||
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.dimension.DimensionType;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.entity.EntityType;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.item.ItemType;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.configuration.FinishConfigurationPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.configuration.RegistryDataPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.login.LoginSuccessPacket;
|
||||
@@ -47,38 +50,53 @@ public class FallbackPreparer {
|
||||
public static final int PLAYER_ENTITY_ID = RANDOM.nextInt();
|
||||
public static final int VEHICLE_ENTITY_ID = RANDOM.nextInt();
|
||||
|
||||
public static final CompoundBinaryTag MAP_ITEM_NBT = CompoundBinaryTag.builder()
|
||||
.put("map", IntBinaryTag.intBinaryTag(0)) // map type
|
||||
.build();
|
||||
|
||||
public static final BlockType[] POSSIBLE_BLOCK_TYPES = BlockType.values();
|
||||
public final FallbackPacket[] BLOCKS_PACKETS = new FallbackPacket[POSSIBLE_BLOCK_TYPES.length];
|
||||
|
||||
public final int BLOCKS_PER_ROW = 8; // 8 * 8 = 64 (vanilla protocol maximum)
|
||||
public final int SPAWN_X_POSITION = 16 / 2; // middle of the chunk
|
||||
public final int SPAWN_Z_POSITION = 16 / 2; // middle of the chunk
|
||||
public final int DEFAULT_Y_COLLIDE_POSITION = 1 + RANDOM.nextInt(255); // 255 is the maximum Y position
|
||||
public final int IN_AIR_Y_POSITION = 1000 + RANDOM.nextInt(338); // High altitude (randomized)
|
||||
public final int IN_VOID_Y_POSITION = -(120 + RANDOM.nextInt(120)); // Low altitude (randomized)
|
||||
public final int PLATFORM_Y_POSITION = 1 + RANDOM.nextInt(255); // 255 is the maximum Y position
|
||||
public final int IN_AIR_Y_POSITION = 3000 + RANDOM.nextInt(500); // Random high altitude
|
||||
|
||||
public final int FIRST_TELEPORT_ID = RANDOM.nextInt();
|
||||
public final int SECOND_TELEPORT_ID = RANDOM.nextInt();
|
||||
public final int PRE_JOIN_KEEP_ALIVE_ID = RANDOM.nextInt();
|
||||
|
||||
private final int MAP_SLOT = RANDOM.nextInt(9);
|
||||
|
||||
public static final FallbackPacket CAPTCHA_SET_CONTAINER_SLOT = new SetContainerSlotPacket(
|
||||
0, 36 + MAP_SLOT, 1, ItemType.FILLED_MAP, CompoundBinaryTag.builder()
|
||||
.put("map", IntBinaryTag.intBinaryTag(0)) // map id
|
||||
.build());
|
||||
public final static FallbackPacket CAPTCHA_HELD_ITEM_SLOT = new SetHeldItemPacket(MAP_SLOT);
|
||||
public final FallbackPacket DEFAULT_ABILITIES = new PlayerAbilitiesPacket(0x00, 0, 0);
|
||||
public final FallbackPacket NO_MOVE_ABILITIES = new PlayerAbilitiesPacket(0x02, 0, 0);
|
||||
public final FallbackPacket NO_MOVE_ABILITIES_BEDROCK = new PlayerAbilitiesPacket(0x06, 0, 0);
|
||||
public final FallbackPacket CAPTCHA_POSITION = new FallbackPacketSnapshot(new SetPlayerPositionRotationPacket(
|
||||
SPAWN_X_POSITION, IN_AIR_Y_POSITION, SPAWN_Z_POSITION, 0, 90, 0, 0, false));
|
||||
SPAWN_X_POSITION, 10000, SPAWN_Z_POSITION, 0, 90, 0, 0, false, false, true));
|
||||
public final FallbackPacket EMPTY_CHUNK_DATA = new FallbackPacketSnapshot(new ChunkDataPacket(0, 0));
|
||||
public final FallbackPacket FINISH_CONFIGURATION = new FinishConfigurationPacket();
|
||||
public final FallbackPacket REGISTRY_SYNC_LEGACY = new FallbackPacketSnapshot(new RegistryDataPacket(DimensionRegistry.CODEC_1_20, null, null));
|
||||
public final FallbackPacket PRE_JOIN_KEEP_ALIVE = new FallbackPacketSnapshot(new KeepAlivePacket(PRE_JOIN_KEEP_ALIVE_ID));
|
||||
public final FallbackPacket[] REGISTRY_SYNC_1_20 = new FallbackPacket[] {
|
||||
new FallbackPacketSnapshot(new RegistryDataPacket(DimensionRegistry.CODEC_1_20, null, null))};
|
||||
public final FallbackPacket[] REGISTRY_SYNC_1_20_5 = RegistryDataPacket.of(DimensionRegistry.CODEC_1_20);
|
||||
public final FallbackPacket[] REGISTRY_SYNC_1_21 = RegistryDataPacket.of(DimensionRegistry.CODEC_1_21);
|
||||
public final FallbackPacket[] REGISTRY_SYNC_1_21_2 = RegistryDataPacket.of(DimensionRegistry.CODEC_1_21_2);
|
||||
public final FallbackPacket START_WRITING_CHUNKS = new GameEventPacket(13, 0);
|
||||
public final static FallbackPacket CACHED_HELD_ITEM_SLOT = new SetHeldItemPacket(~4);
|
||||
public final FallbackPacket CAPTCHA_KEEP_ALIVE = new KeepAlivePacket(RANDOM.nextInt());
|
||||
public final static FallbackPacket INVALID_HELD_ITEM_SLOT = new SetHeldItemPacket(-1);
|
||||
public final FallbackPacket RANDOM_KEEP_ALIVE = new FallbackPacketSnapshot(new KeepAlivePacket(RANDOM.nextInt()));
|
||||
public static final FallbackPacket SPAWN_BOAT_ENTITY = new FallbackPacketSnapshot(new SpawnEntityPacket(
|
||||
VEHICLE_ENTITY_ID, EntityType.BOAT, SPAWN_X_POSITION, IN_AIR_Y_POSITION, SPAWN_Z_POSITION,
|
||||
0, 0, 0, 0));
|
||||
public static final FallbackPacket SPAWN_MINECART_ENTITY = new FallbackPacketSnapshot(new SpawnEntityPacket(
|
||||
VEHICLE_ENTITY_ID, EntityType.MINECART, SPAWN_X_POSITION, IN_AIR_Y_POSITION - 16, SPAWN_Z_POSITION,
|
||||
0, 0, 0, 0));
|
||||
public static final FallbackPacket REMOVE_VEHICLE = new RemoveEntitiesPacket(VEHICLE_ENTITY_ID);
|
||||
public static final FallbackPacket SET_VEHICLE_PASSENGERS = new FallbackPacketSnapshot(
|
||||
new SetPassengersPacket(VEHICLE_ENTITY_ID, PLAYER_ENTITY_ID));
|
||||
|
||||
public static FallbackPacket loginSuccess;
|
||||
public FallbackPacket loginSuccess;
|
||||
public FallbackPacket welcomeMessage;
|
||||
public FallbackPacket enterCodeMessage;
|
||||
public FallbackPacket incorrectCaptcha;
|
||||
@@ -93,14 +111,10 @@ public class FallbackPreparer {
|
||||
public FallbackPacket alreadyVerifying;
|
||||
public FallbackPacket reconnectedTooFast;
|
||||
public FallbackPacket protocolBlacklisted;
|
||||
public FallbackPacket invalidUsername;
|
||||
public FallbackPacket unsupportedVersion;
|
||||
public FallbackPacket tooManyOnlinePerIP;
|
||||
public FallbackPacket removeBoat;
|
||||
public FallbackPacket teleportMinecart;
|
||||
public FallbackPacket spawnBoatEntity;
|
||||
public FallbackPacket spawnMinecartEntity;
|
||||
public FallbackPacket setPassengers;
|
||||
public FallbackPacket[] xpCountdown;
|
||||
public FallbackPacket updateTime;
|
||||
|
||||
public static int maxMovementTick, dynamicSpawnYPosition, maxTotalPacketsSent;
|
||||
|
||||
@@ -110,16 +124,23 @@ public class FallbackPreparer {
|
||||
Sonar.get().getLogger().info("Preloading all registered packets...");
|
||||
FallbackPacketRegistry.values();
|
||||
|
||||
// Prepare LoginSuccess packet
|
||||
loginSuccess = new FallbackPacketSnapshot(new LoginSuccessPacket(UUID.randomUUID(),
|
||||
Sonar.get().getConfig().getGeneralConfig().getString("verification.cached-username")));
|
||||
// Prepare LoginSuccess packet with capped username to 16 characters
|
||||
final UUID uuid = UUID.randomUUID();
|
||||
String username = Sonar.get().getConfig().getGeneralConfig().getString("verification.cached-username");
|
||||
if (username.length() > 16) {
|
||||
username = username.substring(0, 16);
|
||||
}
|
||||
loginSuccess = new FallbackPacketSnapshot(new LoginSuccessPacket(uuid, username, true));
|
||||
|
||||
// Prepare JoinGame packet
|
||||
joinGame = new FallbackPacketSnapshot(new JoinGamePacket(PLAYER_ENTITY_ID,
|
||||
Sonar.get().getConfig().getVerification().getGamemode().getId(), RANDOM.nextLong(),
|
||||
0, 0, false, true, false,
|
||||
false, false, false, true,
|
||||
new String[]{"minecraft:overworld"}, "minecraft:overworld", "flat"));
|
||||
Sonar.get().getConfig().getVerification().getGamemode().getId(),
|
||||
-1, 0, 0,
|
||||
RANDOM.nextInt(3), 1, 0, 0,
|
||||
new String[]{"lol"}, "lol", "flat",
|
||||
DimensionType.OVERWORLD, RANDOM.nextLong() & 1337,
|
||||
false, true, false,
|
||||
false, false, false, true));
|
||||
|
||||
// Prepare the gravity check
|
||||
maxMovementTick = Sonar.get().getConfig().getVerification().getGravity().getMaxMovementTicks();
|
||||
@@ -132,18 +153,18 @@ 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(fallDistance);
|
||||
dynamicSpawnYPosition = PLATFORM_Y_POSITION + (int) Math.ceil(fallDistance);
|
||||
defaultSpawnPosition = new FallbackPacketSnapshot(new SetDefaultSpawnPositionPacket(
|
||||
SPAWN_X_POSITION, IN_AIR_Y_POSITION, SPAWN_Z_POSITION));
|
||||
spawnPosition = new FallbackPacketSnapshot(new SetPlayerPositionRotationPacket(
|
||||
SPAWN_X_POSITION, IN_AIR_Y_POSITION, SPAWN_Z_POSITION,
|
||||
0, 0, FIRST_TELEPORT_ID, 0, false));
|
||||
0, 0, FIRST_TELEPORT_ID, 0, false, false, true));
|
||||
fallStartPosition = new FallbackPacketSnapshot(new SetPlayerPositionRotationPacket(
|
||||
SPAWN_X_POSITION, dynamicSpawnYPosition - IN_AIR_Y_POSITION, SPAWN_Z_POSITION,
|
||||
0, -90, SECOND_TELEPORT_ID, 1 << 1, false));
|
||||
0, -90, SECOND_TELEPORT_ID, 1 << 1, false, false, true));
|
||||
fallStartPositionLegacy = new FallbackPacketSnapshot(new SetPlayerPositionRotationPacket(
|
||||
SPAWN_X_POSITION, dynamicSpawnYPosition, SPAWN_Z_POSITION,
|
||||
0, -90, 0, 0, false));
|
||||
0, -90, 0, 0, false, false, true));
|
||||
|
||||
// Prepare collision platform positions
|
||||
final int length = BLOCKS_PER_ROW / 2;
|
||||
@@ -154,21 +175,21 @@ public class FallbackPreparer {
|
||||
for (int x = 0; x < BLOCKS_PER_ROW; x++) {
|
||||
for (int z = 0; z < BLOCKS_PER_ROW; z++) {
|
||||
final BlockUpdate.BlockPosition position = new BlockUpdate.BlockPosition(
|
||||
x + length, DEFAULT_Y_COLLIDE_POSITION, z + length, 0, 0);
|
||||
x + length, PLATFORM_Y_POSITION, z + length, 0, 0);
|
||||
blocks[index++] = new BlockUpdate(position, POSSIBLE_BLOCK_TYPES[i]);
|
||||
}
|
||||
}
|
||||
BLOCKS_PACKETS[i] = new UpdateSectionBlocksPacket(0, 0, blocks);
|
||||
BLOCKS_PACKETS[i] = new FallbackPacketSnapshot(new UpdateSectionBlocksPacket(0, 0, blocks));
|
||||
}
|
||||
|
||||
// Prepare disconnect packets during login
|
||||
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));
|
||||
blacklisted = new FallbackPacketSnapshot(new DisconnectPacket(Sonar.get().getConfig().getVerification().getBlacklisted(), true));
|
||||
alreadyVerifying = new FallbackPacketSnapshot(new DisconnectPacket(Sonar.get().getConfig().getVerification().getAlreadyVerifying(), true));
|
||||
alreadyQueued = new FallbackPacketSnapshot(new DisconnectPacket(Sonar.get().getConfig().getVerification().getAlreadyQueued(), true));
|
||||
protocolBlacklisted = new FallbackPacketSnapshot(new DisconnectPacket(Sonar.get().getConfig().getVerification().getProtocolBlacklisted(), true));
|
||||
reconnectedTooFast = new FallbackPacketSnapshot(new DisconnectPacket(Sonar.get().getConfig().getVerification().getTooFastReconnect(), true));
|
||||
unsupportedVersion = new FallbackPacketSnapshot(new DisconnectPacket(Sonar.get().getConfig().getVerification().getUnsupportedVersion(), true));
|
||||
tooManyOnlinePerIP = new FallbackPacketSnapshot(new DisconnectPacket(Sonar.get().getConfig().getTooManyOnlinePerIp(), true));
|
||||
|
||||
// Prepare transfer packet
|
||||
if (Sonar.get().getConfig().getGeneralConfig().getBoolean("verification.transfer.enabled")) {
|
||||
@@ -179,17 +200,13 @@ public class FallbackPreparer {
|
||||
transferToOrigin = null;
|
||||
}
|
||||
|
||||
// Prepare packets for the vehicle check
|
||||
removeBoat = new RemoveEntitiesPacket(VEHICLE_ENTITY_ID);
|
||||
teleportMinecart = new TeleportEntityPacket(
|
||||
VEHICLE_ENTITY_ID, SPAWN_X_POSITION, IN_VOID_Y_POSITION, SPAWN_Z_POSITION, false);
|
||||
spawnBoatEntity = new FallbackPacketSnapshot(new SpawnEntityPacket(VEHICLE_ENTITY_ID,
|
||||
EntityType.BOAT, RANDOM.nextInt(16), IN_AIR_Y_POSITION, RANDOM.nextInt(16),
|
||||
0, 0, 0, 0));
|
||||
spawnMinecartEntity = new FallbackPacketSnapshot(new SpawnEntityPacket(VEHICLE_ENTITY_ID,
|
||||
EntityType.MINECART, RANDOM.nextInt(16), IN_AIR_Y_POSITION, RANDOM.nextInt(16),
|
||||
0, 0, 0, 0));
|
||||
setPassengers = new FallbackPacketSnapshot(new SetPassengersPacket(VEHICLE_ENTITY_ID, PLAYER_ENTITY_ID));
|
||||
// Prepare update time packet
|
||||
final int timeOfDay = Sonar.get().getConfig().getVerification().getTimeOfDay();
|
||||
if (timeOfDay != 1000) {
|
||||
updateTime = new UpdateTimePacket(0L, timeOfDay, false);
|
||||
} else {
|
||||
updateTime = null;
|
||||
}
|
||||
|
||||
// If the welcome message is empty, we don't need to send a message to the player
|
||||
final String welcome = Sonar.get().getConfig().getMessagesConfig().getString("verification.welcome");
|
||||
@@ -250,4 +267,17 @@ public class FallbackPreparer {
|
||||
+ Sonar.get().getConfig().getVerification().getMap().getMaxTries()
|
||||
+ 150 /* some arbitrary leeway */;
|
||||
}
|
||||
|
||||
public static FallbackPacket[] getRegistryPackets(final @NotNull ProtocolVersion protocolVersion) {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_2)) {
|
||||
return REGISTRY_SYNC_1_21_2;
|
||||
}
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21)) {
|
||||
return REGISTRY_SYNC_1_21;
|
||||
}
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_20_5)) {
|
||||
return REGISTRY_SYNC_1_20_5;
|
||||
}
|
||||
return REGISTRY_SYNC_1_20;
|
||||
}
|
||||
}
|
||||
+103
-82
@@ -36,251 +36,272 @@ public enum BlockType {
|
||||
// - https://github.com/ViaVersion/Mappings/tree/main/mappings
|
||||
//STONE(protocolVersion -> 1, 1),
|
||||
ENCHANTMENT_TABLE(protocolVersion -> {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_12_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
|
||||
return 116;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_1)) {
|
||||
return 4612;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_2)) {
|
||||
return 4613;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_15_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_15_2)) {
|
||||
return 5116;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_16_1)) {
|
||||
return 5132;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16_4) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_16_4)) {
|
||||
return 5136;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_18_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_18_2)) {
|
||||
return 5333;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_1)) {
|
||||
return 5719;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_3) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_3)) {
|
||||
return 7159;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_4) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_4)) {
|
||||
return 7385;
|
||||
}
|
||||
return 7389;
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21)) {
|
||||
return 7389;
|
||||
}
|
||||
return 7619;
|
||||
}, protocolVersion -> (double) 0.75f),
|
||||
TRAPDOOR(protocolVersion -> {
|
||||
// We have to use wooden trapdoors for 1.7 since 1.7 doesn't have iron trapdoors
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_7_6) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_7_6)) {
|
||||
return 96;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_12_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
|
||||
return 167;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_1)) {
|
||||
return 6509;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_2)) {
|
||||
return 6510;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_15_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_15_2)) {
|
||||
return 7016;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_16_1)) {
|
||||
return 7552;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16_4) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_16_4)) {
|
||||
return 7556;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_18_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_18_2)) {
|
||||
return 7802;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_1)) {
|
||||
return 8293;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_3) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_3)) {
|
||||
return 9937;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_4) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_4)) {
|
||||
return 10269;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_20) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_20)) {
|
||||
return 10273;
|
||||
}
|
||||
return 10414;
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21)) {
|
||||
return 10414;
|
||||
}
|
||||
return 10749;
|
||||
}, protocolVersion -> 0.1875),
|
||||
END_PORTAL_FRAME(protocolVersion -> {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_12_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
|
||||
return 120;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_1)) {
|
||||
return 4633;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_2)) {
|
||||
return 4634;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_15_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_15_2)) {
|
||||
return 5137;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_16_1)) {
|
||||
return 5153;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16_4) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_16_4)) {
|
||||
return 5157;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_18_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_18_2)) {
|
||||
return 5358;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_1)) {
|
||||
return 5744;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_3) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_3)) {
|
||||
return 7184;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_4) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_4)) {
|
||||
return 7410;
|
||||
}
|
||||
return 7414;
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21)) {
|
||||
return 7414;
|
||||
}
|
||||
return 7644;
|
||||
}, protocolVersion -> 0.8125),
|
||||
DAYLIGHT_SENSOR(protocolVersion -> {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_12_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
|
||||
return 151;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_1)) {
|
||||
return 5651;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_2)) {
|
||||
return 5652;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_15_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_15_2)) {
|
||||
return 6158;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_16_1)) {
|
||||
return 6694;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16_4) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_16_4)) {
|
||||
return 6698;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_18_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_18_2)) {
|
||||
return 6916;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_1)) {
|
||||
return 7327;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_3) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_3)) {
|
||||
return 8811;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_4) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_4)) {
|
||||
return 9063;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_20) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_20)) {
|
||||
return 9067;
|
||||
}
|
||||
return 9207;
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21)) {
|
||||
return 9207;
|
||||
}
|
||||
return 9462;
|
||||
}, protocolVersion -> 0.375),
|
||||
COBBLESTONE_WALL(protocolVersion -> {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_12_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
|
||||
return 139;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_1)) {
|
||||
return 5196;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_2)) {
|
||||
return 5197;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_15_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_15_2)) {
|
||||
return 5700;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_16_1)) {
|
||||
return 5660;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16_4) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_16_4)) {
|
||||
return 5664;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_18_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_18_2)) {
|
||||
return 5866;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_1)) {
|
||||
return 6252;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_3) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_3)) {
|
||||
return 7692;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_4) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_4)) {
|
||||
return 7918;
|
||||
}
|
||||
return 7922;
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21)) {
|
||||
return 7922;
|
||||
}
|
||||
return 8152;
|
||||
}, protocolVersion -> 1.5),
|
||||
STONE_SLABS(protocolVersion -> {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_12_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
|
||||
return 44;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_1)) {
|
||||
return 7296;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_2)) {
|
||||
return 7297;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_15_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_15_2)) {
|
||||
return 7809;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_16_1)) {
|
||||
return 8345;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16_4) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_16_4)) {
|
||||
return 8349;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_18_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_18_2)) {
|
||||
return 8595;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_1)) {
|
||||
return 9092;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_3) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_3)) {
|
||||
return 10748;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_4) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_4)) {
|
||||
return 11086;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_20) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_20)) {
|
||||
return 11090;
|
||||
}
|
||||
return 11231;
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21)) {
|
||||
return 11231;
|
||||
}
|
||||
return 11566;
|
||||
}, protocolVersion -> 0.5),
|
||||
WHITE_CARPET(protocolVersion -> {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_12_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
|
||||
return 171;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_1)) {
|
||||
return 6823;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_2)) {
|
||||
return 6824;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_15_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_15_2)) {
|
||||
return 7330;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_16_1)) {
|
||||
return 7866;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16_4) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_16_4)) {
|
||||
return 7870;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_18_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_18_2)) {
|
||||
return 8116;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_1)) {
|
||||
return 8607;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_3) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_3)) {
|
||||
return 10251;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_4) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_4)) {
|
||||
return 10583;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_20) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_20)) {
|
||||
return 10587;
|
||||
}
|
||||
return 10728;
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21)) {
|
||||
return 10728;
|
||||
}
|
||||
return 11063;
|
||||
}, protocolVersion -> protocolVersion.compareTo(MINECRAFT_1_8) < 0 ? 0 : 0.0625);
|
||||
|
||||
private final Function<ProtocolVersion, Integer> id;
|
||||
|
||||
+1
-3
@@ -19,12 +19,11 @@ 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 BlockUpdate {
|
||||
private final @NotNull BlockPosition position;
|
||||
private final BlockPosition position;
|
||||
private final BlockType blockType;
|
||||
private final int legacyBlockState, blockState;
|
||||
|
||||
@@ -39,7 +38,6 @@ public final class BlockUpdate {
|
||||
}
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@RequiredArgsConstructor
|
||||
public static final class BlockPosition {
|
||||
private final int x, y, z;
|
||||
|
||||
+2
-14
@@ -18,10 +18,8 @@
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.dimension;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import net.kyori.adventure.nbt.BinaryTag;
|
||||
import net.kyori.adventure.nbt.BinaryTagIO;
|
||||
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||
import net.kyori.adventure.nbt.ListBinaryTag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
|
||||
@@ -38,9 +36,7 @@ public final class DimensionRegistry {
|
||||
public final CompoundBinaryTag CODEC_1_19_4;
|
||||
public final CompoundBinaryTag CODEC_1_20;
|
||||
public final CompoundBinaryTag CODEC_1_21;
|
||||
|
||||
public final DimensionInfo DEFAULT_DIMENSION_1_16_2;
|
||||
public final DimensionInfo DEFAULT_DIMENSION_1_18_2;
|
||||
public final CompoundBinaryTag CODEC_1_21_2;
|
||||
|
||||
static {
|
||||
CODEC_1_16 = getCodec("codec_1_16.nbt");
|
||||
@@ -51,9 +47,7 @@ public final class DimensionRegistry {
|
||||
CODEC_1_19_4 = getCodec("codec_1_19_4.nbt");
|
||||
CODEC_1_20 = getCodec("codec_1_20.nbt");
|
||||
CODEC_1_21 = getCodec("codec_1_21.nbt");
|
||||
|
||||
DEFAULT_DIMENSION_1_16_2 = getDimension(CODEC_1_16_2);
|
||||
DEFAULT_DIMENSION_1_18_2 = getDimension(CODEC_1_18_2);
|
||||
CODEC_1_21_2 = getCodec("codec_1_21_2.nbt");
|
||||
}
|
||||
|
||||
private @NotNull CompoundBinaryTag getCodec(final @NotNull String fileName) {
|
||||
@@ -64,10 +58,4 @@ public final class DimensionRegistry {
|
||||
throw new IllegalStateException(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
private static @NotNull DimensionInfo getDimension(final @NotNull CompoundBinaryTag tag) {
|
||||
final ListBinaryTag dimensions = tag.getCompound("minecraft:dimension_type").getList("value");
|
||||
final BinaryTag elementTag = ((CompoundBinaryTag) dimensions.get(0)).get("element");
|
||||
return new DimensionInfo("minecraft:overworld", 0, (CompoundBinaryTag) elementTag);
|
||||
}
|
||||
}
|
||||
+7
-7
@@ -19,14 +19,14 @@ package xyz.jonesdev.sonar.common.fallback.protocol.dimension;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@RequiredArgsConstructor
|
||||
public final class DimensionInfo {
|
||||
private final String identifier;
|
||||
private final int id;
|
||||
private final CompoundBinaryTag tag;
|
||||
public enum DimensionType {
|
||||
OVERWORLD("minecraft:overworld", 0, 0),
|
||||
THE_NETHER("minecraft:the_nether", -1, 1),
|
||||
THE_END("minecraft:the_end", 1, 2);
|
||||
|
||||
private final String key;
|
||||
private final int legacyId, id;
|
||||
}
|
||||
+28
-25
@@ -17,8 +17,8 @@
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.entity;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
|
||||
import java.util.function.Function;
|
||||
@@ -26,66 +26,69 @@ import java.util.function.Function;
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
|
||||
// TODO: load mappings from a separate file
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum EntityType {
|
||||
// Useful resources:
|
||||
// - https://github.com/PrismarineJS/minecraft-data/blob/master/data/pc/
|
||||
// - https://pokechu22.github.io/Burger/1.20.4.html
|
||||
BOAT(protocolVersion -> {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_2)) {
|
||||
return 1;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_14_4) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_14_4)) {
|
||||
return 5;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16_4) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_16_4)) {
|
||||
return 6;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_18_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_18_2)) {
|
||||
return 7;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_3) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_3)) {
|
||||
return 8;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_20_3) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_20_3)) {
|
||||
return 9;
|
||||
}
|
||||
return 10;
|
||||
}),
|
||||
MINECART(protocolVersion -> {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21)) {
|
||||
return 10;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_14_4) <= 0) {
|
||||
return 85; // 1.21.2 split the boat type in id registries.
|
||||
}),
|
||||
MINECART(protocolVersion -> {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_2)) {
|
||||
return 10;
|
||||
}
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_14_4)) {
|
||||
return 41;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_15_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_15_2)) {
|
||||
return 42;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16_4) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_16_4)) {
|
||||
return 45;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_18_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_18_2)) {
|
||||
return 50;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_1)) {
|
||||
return 53;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_3) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_3)) {
|
||||
return 54;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_20_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_20_2)) {
|
||||
return 64;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_20_3) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_20_3)) {
|
||||
return 65;
|
||||
}
|
||||
return 69;
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21)) {
|
||||
return 69;
|
||||
}
|
||||
return 82;
|
||||
});
|
||||
|
||||
private final Function<ProtocolVersion, Integer> function;
|
||||
|
||||
public int getId(final @NotNull ProtocolVersion protocolVersion) {
|
||||
return function.apply(protocolVersion);
|
||||
}
|
||||
private final Function<ProtocolVersion, Integer> id;
|
||||
}
|
||||
+21
-20
@@ -15,10 +15,10 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.captcha;
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.item;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
|
||||
import java.util.function.Function;
|
||||
@@ -26,6 +26,7 @@ import java.util.function.Function;
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
|
||||
// TODO: load mappings from a separate file
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum ItemType {
|
||||
// Useful resources:
|
||||
@@ -33,45 +34,45 @@ public enum ItemType {
|
||||
// - https://pokechu22.github.io/Burger/1.20.4.html
|
||||
// - https://github.com/ViaVersion/Mappings/tree/main/mappings
|
||||
FILLED_MAP(protocolVersion -> {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_12_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
|
||||
return 358;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_1)) {
|
||||
return 608;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_2)) {
|
||||
return 613;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_15_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_15_2)) {
|
||||
return 671;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16_4) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_16_4)) {
|
||||
return 733;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_18_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_18_2)) {
|
||||
return 847;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_1) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_1)) {
|
||||
return 886;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_3) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_3)) {
|
||||
return 914;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_4) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_19_4)) {
|
||||
return 937;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_20_2) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_20_2)) {
|
||||
return 941;
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_20_3) <= 0) {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_20_3)) {
|
||||
return 979;
|
||||
}
|
||||
return 982;
|
||||
});
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21)) {
|
||||
return 982;
|
||||
}
|
||||
return 1022;
|
||||
}, protocolVersion -> protocolVersion.greaterThanOrEquals(MINECRAFT_1_21_2) ? 36 : 26);
|
||||
|
||||
private final Function<ProtocolVersion, Integer> idFunction;
|
||||
|
||||
public int getId(final @NotNull ProtocolVersion protocolVersion) {
|
||||
return idFunction.apply(protocolVersion);
|
||||
}
|
||||
private final Function<ProtocolVersion, Integer> id;
|
||||
private final Function<ProtocolVersion, Integer> components;
|
||||
}
|
||||
+7
-9
@@ -15,16 +15,15 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.captcha;
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.map;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.play.MapDataPacket;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_8;
|
||||
|
||||
@Getter
|
||||
public final class MapCaptchaInfo {
|
||||
private final String answer;
|
||||
@@ -42,22 +41,21 @@ public final class MapCaptchaInfo {
|
||||
}
|
||||
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 MapDataPacket(grid[i], i, 0, 0, true);
|
||||
}
|
||||
|
||||
// Prepare 1.8+ map data
|
||||
this.modern = new MapDataPacket(buffer, 0, 0);
|
||||
this.modern = new MapDataPacket(buffer, 0, 0, 0, true);
|
||||
}
|
||||
|
||||
public void delayedWrite(final @NotNull FallbackUser user) {
|
||||
if (user.getProtocolVersion().compareTo(MINECRAFT_1_8) < 0) {
|
||||
if (user.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
// 1.7.2-1.7.10 needs separate packets for each axis
|
||||
for (final FallbackPacket legacyPacket : legacy) {
|
||||
user.delayedWrite(legacyPacket);
|
||||
for (final FallbackPacket packet : legacy) {
|
||||
user.delayedWrite(packet);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Send modern packet
|
||||
user.delayedWrite(modern);
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.captcha;
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.map;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -26,6 +26,7 @@ import java.awt.image.Raster;
|
||||
@UtilityClass
|
||||
public class MapColorPalette {
|
||||
// https://minecraft.wiki/w/Map_item_format#Full_color_tables
|
||||
// TODO: load mappings from a separate file
|
||||
public static final int[] COLOR_MAP = {
|
||||
-1,
|
||||
-1,
|
||||
+1
-1
@@ -32,7 +32,7 @@ public final class FinishConfigurationPacket implements FallbackPacket {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int expectedMaxLength(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
public int expectedMaxLength(final ProtocolVersion protocolVersion) {
|
||||
// This packet is always empty; see https://wiki.vg/Protocol#Finish_Configuration
|
||||
return 0;
|
||||
}
|
||||
|
||||
+9
-9
@@ -26,12 +26,11 @@ 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 xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.*;
|
||||
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@@ -42,19 +41,19 @@ public final class RegistryDataPacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void encode(final @NotNull ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_20_5) < 0) {
|
||||
writeNamelessCompoundTag(byteBuf, tag);
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_5)) {
|
||||
ProtocolUtil.writeBinaryTag(byteBuf, protocolVersion, tag);
|
||||
} else if (type != null) {
|
||||
writeString(byteBuf, type);
|
||||
writeVarInt(byteBuf, bundles.size());
|
||||
ProtocolUtil.writeString(byteBuf, type);
|
||||
ProtocolUtil.writeVarInt(byteBuf, bundles.size());
|
||||
|
||||
for (final RegistryDataPacket.Bundle bundle : bundles) {
|
||||
writeString(byteBuf, bundle.getName());
|
||||
ProtocolUtil.writeString(byteBuf, bundle.getName());
|
||||
// Write the bundle tag
|
||||
final CompoundBinaryTag tag = bundle.getTag();
|
||||
if (tag != null) {
|
||||
byteBuf.writeBoolean(true);
|
||||
writeNamelessCompoundTag(byteBuf, tag);
|
||||
ProtocolUtil.writeBinaryTag(byteBuf, protocolVersion, tag);
|
||||
} else {
|
||||
byteBuf.writeBoolean(false);
|
||||
}
|
||||
@@ -74,7 +73,8 @@ public final class RegistryDataPacket implements FallbackPacket {
|
||||
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")));
|
||||
// CompoundBinaryTag#getCompound(String) will return an empty compound tag when not exist.
|
||||
bundles.add(new Bundle(tag.getString("name"), tag.get("element") == null ? null : tag.getCompound("element")));
|
||||
}
|
||||
packets[index++] = new FallbackPacketSnapshot(new RegistryDataPacket(rootTag, type, bundles));
|
||||
}
|
||||
|
||||
+4
-6
@@ -22,9 +22,7 @@ import lombok.Getter;
|
||||
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.util.ProtocolUtil.readString;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.readVarInt;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString(of = {"protocolVersionId", "hostname", "port"})
|
||||
@@ -49,9 +47,9 @@ public final class HandshakePacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void decode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
protocolVersionId = readVarInt(byteBuf);
|
||||
hostname = readString(byteBuf, MAXIMUM_HOSTNAME_LENGTH);
|
||||
protocolVersionId = ProtocolUtil.readVarInt(byteBuf);
|
||||
hostname = ProtocolUtil.readString(byteBuf, MAXIMUM_HOSTNAME_LENGTH);
|
||||
port = byteBuf.readUnsignedShort();
|
||||
intent = readVarInt(byteBuf);
|
||||
intent = ProtocolUtil.readVarInt(byteBuf);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -33,7 +33,7 @@ public final class LoginAcknowledgedPacket implements FallbackPacket {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int expectedMaxLength(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
public int expectedMaxLength(final ProtocolVersion protocolVersion) {
|
||||
// This packet is always empty; see https://wiki.vg/Protocol#Login_Acknowledged
|
||||
return 0;
|
||||
}
|
||||
|
||||
+22
-20
@@ -18,6 +18,7 @@
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.login;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -26,11 +27,11 @@ 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.util.ProtocolUtil;
|
||||
import xyz.jonesdev.sonar.common.util.exception.QuietDecoderException;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.*;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@@ -46,31 +47,32 @@ public final class LoginStartPacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void decode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) {
|
||||
username = readString(byteBuf, 16);
|
||||
username = ProtocolUtil.readString(byteBuf, 16);
|
||||
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19) >= 0) {
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19_3) < 0) {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_19)) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) {
|
||||
if (byteBuf.readBoolean()) {
|
||||
readPlayerKey(byteBuf);
|
||||
final long expiry = byteBuf.readLong();
|
||||
final long now = System.currentTimeMillis();
|
||||
if (expiry < now) {
|
||||
throw ProtocolUtil.DEBUG ? new DecoderException("Bad expiry: " + expiry) : QuietDecoderException.INSTANCE;
|
||||
}
|
||||
final byte[] key = ProtocolUtil.readByteArray(byteBuf);
|
||||
if (key.length == 0) {
|
||||
throw ProtocolUtil.DEBUG ? new DecoderException("Empty key") : QuietDecoderException.INSTANCE;
|
||||
}
|
||||
final byte[] signature = ProtocolUtil.readByteArray(byteBuf, 4096);
|
||||
if (signature.length == 0) {
|
||||
throw ProtocolUtil.DEBUG ? new DecoderException("Empty signature") : QuietDecoderException.INSTANCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_20_2) >= 0) {
|
||||
uuid = readUUID(byteBuf);
|
||||
return;
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
|
||||
if (byteBuf.readBoolean()) {
|
||||
uuid = readUUID(byteBuf);
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_19_1)) {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_20_2) || byteBuf.readBoolean()) {
|
||||
uuid = ProtocolUtil.readUUID(byteBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void readPlayerKey(final @NotNull ByteBuf byteBuf) {
|
||||
final long expiry = byteBuf.readLong();
|
||||
final byte[] key = readByteArray(byteBuf);
|
||||
final byte[] signature = readByteArray(byteBuf, 4096);
|
||||
}
|
||||
}
|
||||
+16
-19
@@ -26,12 +26,10 @@ 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.util.FastUuidSansHyphens;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.*;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@@ -39,30 +37,29 @@ import static xyz.jonesdev.sonar.common.util.ProtocolUtil.*;
|
||||
public final class LoginSuccessPacket implements FallbackPacket {
|
||||
private UUID uuid;
|
||||
private String username;
|
||||
private boolean strictErrorHandling;
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19) >= 0) {
|
||||
writeUUID(byteBuf, uuid);
|
||||
} else if (protocolVersion.compareTo(MINECRAFT_1_16) >= 0) {
|
||||
writeUUIDIntArray(byteBuf, uuid);
|
||||
} else if (protocolVersion.compareTo(MINECRAFT_1_7_6) >= 0) {
|
||||
writeString(byteBuf, uuid.toString());
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_16)) {
|
||||
ProtocolUtil.writeUUID(byteBuf, uuid);
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_7_6)) {
|
||||
ProtocolUtil.writeString(byteBuf, uuid.toString());
|
||||
} else {
|
||||
writeString(byteBuf, FastUuidSansHyphens.toString(uuid));
|
||||
ProtocolUtil.writeString(byteBuf, FastUuidSansHyphens.toString(uuid));
|
||||
}
|
||||
|
||||
// Cap the length of the username to 16 characters
|
||||
if (username.length() > 16) {
|
||||
username = username.substring(0, 16);
|
||||
}
|
||||
writeString(byteBuf, username);
|
||||
ProtocolUtil.writeString(byteBuf, username);
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19) >= 0) {
|
||||
writeVarInt(byteBuf, 0); // properties
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_19)) {
|
||||
// We don't need to send any properties to the client
|
||||
ProtocolUtil.writeVarInt(byteBuf, 0);
|
||||
}
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_20_5) >= 0) {
|
||||
byteBuf.writeBoolean(false); // should authenticate
|
||||
|
||||
if (protocolVersion.equals(ProtocolVersion.MINECRAFT_1_20_5)
|
||||
|| protocolVersion.equals(ProtocolVersion.MINECRAFT_1_21)) {
|
||||
// Whether the client should disconnect on its own if it receives invalid data from the server
|
||||
byteBuf.writeBoolean(strictErrorHandling);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-6
@@ -22,9 +22,7 @@ import lombok.*;
|
||||
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.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_8;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.readVarInt;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@@ -45,12 +43,12 @@ public final class AnimationPacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void decode(final @NotNull ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_8) < 0) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
entityId = byteBuf.readInt();
|
||||
type = LegacyAnimationType.getById(byteBuf.readByte());
|
||||
} else if (protocolVersion.compareTo(MINECRAFT_1_8) > 0) {
|
||||
} else if (protocolVersion.greaterThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
// Only 1.9+ clients have an offhand
|
||||
hand = readVarInt(byteBuf);
|
||||
hand = ProtocolUtil.readVarInt(byteBuf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+46
-68
@@ -27,9 +27,7 @@ import net.kyori.adventure.nbt.LongArrayBinaryTag;
|
||||
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.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.*;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@@ -38,70 +36,46 @@ import static xyz.jonesdev.sonar.common.util.ProtocolUtil.*;
|
||||
public final class ChunkDataPacket implements FallbackPacket {
|
||||
private int sectionX, sectionZ;
|
||||
|
||||
private static final byte[] SECTION_BYTES = new byte[]{0, 0, 0, 0, 0, 0, 1, 0};
|
||||
private static final byte[] LIGHT_BYTES = new byte[]{1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 3, -1, -1, 0, 0};
|
||||
|
||||
private static final byte[] LEGACY_FILLER_BYTES_17 = new byte[2];
|
||||
private static final byte[] LEGACY_FILLER_BYTES = new byte[256];
|
||||
private static final byte[] MODERN_FILLER_BYTES = new byte[256 * 4];
|
||||
|
||||
// Prepare nbt for 1.18 and pre-1.18
|
||||
private static final CompoundBinaryTag MODERN_TAG, LEGACY_TAG;
|
||||
|
||||
static {
|
||||
MODERN_TAG = prepareNBT(false);
|
||||
LEGACY_TAG = prepareNBT(true);
|
||||
}
|
||||
|
||||
private static @NotNull CompoundBinaryTag prepareNBT(final boolean legacy) {
|
||||
final long[] arrayData = new long[legacy ? 36 : 37];
|
||||
final LongArrayBinaryTag longArray = LongArrayBinaryTag.longArrayBinaryTag(arrayData);
|
||||
|
||||
final CompoundBinaryTag motion = CompoundBinaryTag.builder()
|
||||
.put("MOTION_BLOCKING", longArray)
|
||||
.build();
|
||||
return CompoundBinaryTag.builder()
|
||||
.put("root", motion)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(final @NotNull ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
byteBuf.writeInt(sectionX);
|
||||
byteBuf.writeInt(sectionZ);
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_17) >= 0) {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_17_1) <= 0) {
|
||||
writeVarInt(byteBuf, 0); // mask
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_17)) {
|
||||
if (protocolVersion.lessThanOrEquals(ProtocolVersion.MINECRAFT_1_17_1)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, 0); // mask
|
||||
}
|
||||
} else {
|
||||
byteBuf.writeBoolean(true); // full chunk
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16) >= 0
|
||||
&& protocolVersion.compareTo(MINECRAFT_1_16_2) < 0) {
|
||||
if (protocolVersion.inBetween(ProtocolVersion.MINECRAFT_1_16, ProtocolVersion.MINECRAFT_1_16_1)) {
|
||||
byteBuf.writeBoolean(true); // ignore old data
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_8) > 0) {
|
||||
writeVarInt(byteBuf, 0);
|
||||
if (protocolVersion.greaterThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, 0);
|
||||
} else {
|
||||
byteBuf.writeShort(1); // fix void chunk
|
||||
}
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_14) >= 0) {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_20_2) >= 0) {
|
||||
writeNamelessCompoundTag(byteBuf, MODERN_TAG);
|
||||
} else {
|
||||
writeCompoundTag(byteBuf, protocolVersion.compareTo(MINECRAFT_1_18) < 0 ? LEGACY_TAG : MODERN_TAG);
|
||||
}
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_14)) {
|
||||
final long[] motionBlockingData = new long[protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_18) ? 36 : 37];
|
||||
final CompoundBinaryTag motionBlockingTag = CompoundBinaryTag.builder()
|
||||
.put("MOTION_BLOCKING", LongArrayBinaryTag.longArrayBinaryTag(motionBlockingData))
|
||||
.build();
|
||||
final CompoundBinaryTag rootTag = CompoundBinaryTag.builder()
|
||||
.put("root", motionBlockingTag)
|
||||
.build();
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_15) >= 0 && protocolVersion.compareTo(MINECRAFT_1_18) < 0) {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16_2) >= 0) {
|
||||
writeVarInt(byteBuf, 1024);
|
||||
ProtocolUtil.writeBinaryTag(byteBuf, protocolVersion, rootTag);
|
||||
|
||||
if (protocolVersion.inBetween(ProtocolVersion.MINECRAFT_1_15, ProtocolVersion.MINECRAFT_1_17_1)) {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_16_2)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, 1024);
|
||||
|
||||
for (int i = 0; i < 1024; i++) {
|
||||
writeVarInt(byteBuf, 1);
|
||||
ProtocolUtil.writeVarInt(byteBuf, 1);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 1024; i++) {
|
||||
@@ -111,36 +85,40 @@ public final class ChunkDataPacket implements FallbackPacket {
|
||||
}
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13) < 0) {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_8) >= 0) {
|
||||
writeByteArray(byteBuf, LEGACY_FILLER_BYTES); // 1.8 - 1.12.2
|
||||
} else {
|
||||
byteBuf.writeInt(0); // compressed size
|
||||
byteBuf.writeBytes(LEGACY_FILLER_BYTES_17); // 1.7
|
||||
}
|
||||
} else if (protocolVersion.compareTo(MINECRAFT_1_15) < 0) {
|
||||
writeByteArray(byteBuf, MODERN_FILLER_BYTES); // 1.13 - 1.14.4
|
||||
} else if (protocolVersion.compareTo(MINECRAFT_1_18) < 0) {
|
||||
writeVarInt(byteBuf, 0); // 1.15 - 1.17.1
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
byteBuf.writeInt(0);
|
||||
byteBuf.writeBytes(new byte[2]);
|
||||
} else if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_15)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, 0);
|
||||
} else if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_18)) {
|
||||
ProtocolUtil.writeByteArray(byteBuf, new byte[256 * 4]);
|
||||
} else {
|
||||
writeVarInt(byteBuf, SECTION_BYTES.length * 16);
|
||||
final byte[] sectionData = new byte[]{0, 0, 0, 0, 0, 0, 1, 0};
|
||||
int count = protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_2) ? 24 : 16;
|
||||
ProtocolUtil.writeVarInt(byteBuf, sectionData.length * count);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
byteBuf.writeBytes(SECTION_BYTES);
|
||||
for (int i = 0; i < count; i++) {
|
||||
byteBuf.writeBytes(sectionData);
|
||||
}
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_9_4) >= 0) {
|
||||
writeVarInt(byteBuf, 0);
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_9_4)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, 0);
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_18) >= 0) {
|
||||
byteBuf.ensureWritable(LIGHT_BYTES.length);
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_2)) {
|
||||
for (int i = 0; i < 6; i++) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, 0);
|
||||
}
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_18)) {
|
||||
final byte[] lightData = new byte[]{1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 3, -1, -1, 0, 0};
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_20) >= 0) {
|
||||
byteBuf.writeBytes(LIGHT_BYTES, 1, LIGHT_BYTES.length - 1);
|
||||
byteBuf.ensureWritable(lightData.length);
|
||||
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_20)) {
|
||||
byteBuf.writeBytes(lightData, 1, lightData.length - 1);
|
||||
} else {
|
||||
byteBuf.writeBytes(LIGHT_BYTES);
|
||||
byteBuf.writeBytes(lightData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+15
-13
@@ -25,10 +25,7 @@ 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.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.readString;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.readVarInt;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@@ -44,6 +41,7 @@ public final class ClientInformationPacket implements FallbackPacket {
|
||||
private int mainHand;
|
||||
private boolean chatFilteringEnabled; // Added in 1.17
|
||||
private boolean clientListingAllowed; // Added in 1.18, overwrites server-list "anonymous" mode
|
||||
private int particleStatus;
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
@@ -52,37 +50,41 @@ public final class ClientInformationPacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void decode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
locale = readString(byteBuf, 16);
|
||||
locale = ProtocolUtil.readString(byteBuf, 16);
|
||||
viewDistance = byteBuf.readByte();
|
||||
chatVisibility = readVarInt(byteBuf);
|
||||
chatVisibility = ProtocolUtil.readVarInt(byteBuf);
|
||||
chatColors = byteBuf.readBoolean();
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_7_6) <= 0) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
difficulty = byteBuf.readByte();
|
||||
}
|
||||
|
||||
skinParts = byteBuf.readUnsignedByte();
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_9) >= 0) {
|
||||
mainHand = readVarInt(byteBuf);
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_9)) {
|
||||
mainHand = ProtocolUtil.readVarInt(byteBuf);
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_17) >= 0) {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_17)) {
|
||||
chatFilteringEnabled = byteBuf.readBoolean();
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_18) >= 0) {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_18)) {
|
||||
clientListingAllowed = byteBuf.readBoolean();
|
||||
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_2)) {
|
||||
particleStatus = ProtocolUtil.readVarInt(byteBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int expectedMinLength(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
public int expectedMinLength(final ProtocolVersion protocolVersion) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int expectedMaxLength(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
public int expectedMaxLength(final ProtocolVersion protocolVersion) {
|
||||
return 0xff + 1; // 256 as a hard-limit
|
||||
}
|
||||
}
|
||||
+20
-10
@@ -15,16 +15,26 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.api.command;
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.play;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
@ToString(of = "rawArguments")
|
||||
public final class CommandInvocation {
|
||||
private final InvocationSource source;
|
||||
private final String[] rawArguments;
|
||||
public final class ClientTickEndPacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int expectedMaxLength(final ProtocolVersion protocolVersion) {
|
||||
// This packet is always empty
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -24,8 +24,7 @@ import lombok.NoArgsConstructor;
|
||||
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.util.ProtocolUtil.readVarInt;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@@ -41,6 +40,6 @@ public final class ConfirmTeleportationPacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void decode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) throws Exception {
|
||||
teleportId = readVarInt(byteBuf);
|
||||
teleportId = ProtocolUtil.readVarInt(byteBuf);
|
||||
}
|
||||
}
|
||||
+7
-7
@@ -21,7 +21,6 @@ import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
@@ -29,24 +28,25 @@ import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.common.util.ComponentHolder;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class DisconnectPacket implements FallbackPacket {
|
||||
private @NotNull ComponentHolder componentHolder;
|
||||
private boolean duringLogin;
|
||||
|
||||
public DisconnectPacket(final @NotNull Component component, final boolean duringLogin) {
|
||||
this.componentHolder = new ComponentHolder(component);
|
||||
this.duringLogin = duringLogin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
componentHolder.write(byteBuf, duringLogin ? ProtocolVersion.MINECRAFT_1_20_2 : protocolVersion);
|
||||
// Force JSON serialization during login phase
|
||||
componentHolder.write(byteBuf, protocolVersion, duringLogin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public static @NotNull DisconnectPacket create(final @NotNull Component component, final boolean duringLogin) {
|
||||
return new DisconnectPacket(new ComponentHolder(component), duringLogin);
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -24,8 +24,7 @@ import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
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;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@@ -37,7 +36,7 @@ public final class EntityAnimationPacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) throws Exception {
|
||||
writeVarInt(byteBuf, entityId);
|
||||
ProtocolUtil.writeVarInt(byteBuf, entityId);
|
||||
byteBuf.writeByte(type.ordinal());
|
||||
}
|
||||
|
||||
|
||||
+105
-190
@@ -21,238 +21,153 @@ import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import net.kyori.adventure.nbt.BinaryTag;
|
||||
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||
import net.kyori.adventure.nbt.ListBinaryTag;
|
||||
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.dimension.DimensionType;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
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.util.ProtocolUtil.*;
|
||||
import java.util.Objects;
|
||||
|
||||
// https://github.com/Nan1t/NanoLimbo/blob/main/src/main/java/ua/nanit/limbo/protocol/packets/play/PacketJoinGame.java
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class JoinGamePacket implements FallbackPacket {
|
||||
private int entityId;
|
||||
private int gamemode;
|
||||
private long partialHashedSeed;
|
||||
private int previousGamemode;
|
||||
private int viewDistance;
|
||||
private int simulationDistance;
|
||||
private boolean isHardcore;
|
||||
private int difficulty;
|
||||
private int maxPlayers;
|
||||
private int portalCooldown;
|
||||
private int seaLevel;
|
||||
private String[] levelNames;
|
||||
private String levelName;
|
||||
private String levelType;
|
||||
private DimensionType dimension;
|
||||
private long partialHashedSeed;
|
||||
private boolean hardcore;
|
||||
private boolean reducedDebugInfo;
|
||||
private boolean showRespawnScreen;
|
||||
private boolean isDebug;
|
||||
private boolean isFlat;
|
||||
private boolean debug;
|
||||
private boolean flat;
|
||||
private boolean limitedCrafting;
|
||||
private boolean secureProfile;
|
||||
private String[] levelNames;
|
||||
private String worldType;
|
||||
private String levelType;
|
||||
|
||||
@Override
|
||||
public void encode(final @NotNull ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
byteBuf.writeInt(entityId);
|
||||
|
||||
if (protocolVersion.inBetween(MINECRAFT_1_7_2, MINECRAFT_1_7_6)) {
|
||||
byteBuf.writeByte(gamemode == 3 ? 1 : gamemode);
|
||||
byteBuf.writeByte(DEFAULT_DIMENSION_1_16_2.getId());
|
||||
byteBuf.writeByte(0); // difficulty
|
||||
byteBuf.writeByte(0); // max players
|
||||
writeString(byteBuf, levelType);
|
||||
return;
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_16_2)) {
|
||||
byteBuf.writeBoolean(hardcore);
|
||||
}
|
||||
|
||||
if (protocolVersion.inBetween(MINECRAFT_1_8, MINECRAFT_1_9)) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
|
||||
byteBuf.writeByte(gamemode);
|
||||
byteBuf.writeByte(DEFAULT_DIMENSION_1_16_2.getId());
|
||||
byteBuf.writeByte(0); // difficulty
|
||||
byteBuf.writeByte(0); // max players
|
||||
writeString(byteBuf, levelType);
|
||||
byteBuf.writeBoolean(reducedDebugInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (protocolVersion.inBetween(MINECRAFT_1_9_1, MINECRAFT_1_13_2)) {
|
||||
byteBuf.writeByte(gamemode);
|
||||
byteBuf.writeInt(DEFAULT_DIMENSION_1_16_2.getId());
|
||||
byteBuf.writeByte(0); // difficulty
|
||||
byteBuf.writeByte(0); // max players
|
||||
writeString(byteBuf, levelType);
|
||||
byteBuf.writeBoolean(reducedDebugInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (protocolVersion.inBetween(MINECRAFT_1_14, MINECRAFT_1_14_4)) {
|
||||
byteBuf.writeByte(gamemode);
|
||||
byteBuf.writeInt(DEFAULT_DIMENSION_1_16_2.getId());
|
||||
byteBuf.writeByte(0); // max players
|
||||
writeString(byteBuf, levelType);
|
||||
writeVarInt(byteBuf, viewDistance);
|
||||
byteBuf.writeBoolean(reducedDebugInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (protocolVersion.inBetween(MINECRAFT_1_15, MINECRAFT_1_15_2)) {
|
||||
byteBuf.writeByte(gamemode);
|
||||
byteBuf.writeInt(DEFAULT_DIMENSION_1_16_2.getId());
|
||||
byteBuf.writeLong(partialHashedSeed);
|
||||
byteBuf.writeByte(0); // max players
|
||||
writeString(byteBuf, levelType);
|
||||
writeVarInt(byteBuf, viewDistance);
|
||||
byteBuf.writeBoolean(reducedDebugInfo);
|
||||
byteBuf.writeBoolean(showRespawnScreen);
|
||||
return;
|
||||
}
|
||||
|
||||
if (protocolVersion.inBetween(MINECRAFT_1_16, MINECRAFT_1_16_1)) {
|
||||
byteBuf.writeByte(gamemode);
|
||||
byteBuf.writeByte(-1); // previous gamemode
|
||||
writeStringArray(byteBuf, levelNames);
|
||||
writeCompoundTag(byteBuf, CODEC_1_16);
|
||||
writeString(byteBuf, DEFAULT_DIMENSION_1_16_2.getIdentifier());
|
||||
writeString(byteBuf, levelType);
|
||||
byteBuf.writeLong(partialHashedSeed);
|
||||
byteBuf.writeByte(0); // max players
|
||||
writeVarInt(byteBuf, viewDistance);
|
||||
byteBuf.writeBoolean(reducedDebugInfo);
|
||||
byteBuf.writeBoolean(showRespawnScreen);
|
||||
byteBuf.writeBoolean(isDebug);
|
||||
byteBuf.writeBoolean(isFlat);
|
||||
return;
|
||||
}
|
||||
|
||||
if (protocolVersion.inBetween(MINECRAFT_1_16_2, MINECRAFT_1_17_1)) {
|
||||
byteBuf.writeBoolean(isHardcore);
|
||||
byteBuf.writeByte(gamemode);
|
||||
byteBuf.writeByte(-1); // previous gamemode
|
||||
writeStringArray(byteBuf, levelNames);
|
||||
writeCompoundTag(byteBuf, CODEC_1_16_2);
|
||||
writeCompoundTag(byteBuf, DEFAULT_DIMENSION_1_16_2.getTag());
|
||||
writeString(byteBuf, levelType);
|
||||
byteBuf.writeLong(partialHashedSeed);
|
||||
writeVarInt(byteBuf, 0); // max players
|
||||
writeVarInt(byteBuf, viewDistance);
|
||||
byteBuf.writeBoolean(reducedDebugInfo);
|
||||
byteBuf.writeBoolean(showRespawnScreen);
|
||||
byteBuf.writeBoolean(isDebug);
|
||||
byteBuf.writeBoolean(isFlat);
|
||||
return;
|
||||
}
|
||||
|
||||
if (protocolVersion.inBetween(MINECRAFT_1_18, MINECRAFT_1_18_2)) {
|
||||
byteBuf.writeBoolean(isHardcore);
|
||||
byteBuf.writeByte(gamemode);
|
||||
byteBuf.writeByte(-1); // previous gamemode
|
||||
writeStringArray(byteBuf, levelNames);
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_18_2) >= 0) {
|
||||
writeCompoundTag(byteBuf, CODEC_1_18_2);
|
||||
writeCompoundTag(byteBuf, DEFAULT_DIMENSION_1_18_2.getTag());
|
||||
} else {
|
||||
writeCompoundTag(byteBuf, CODEC_1_16_2);
|
||||
writeCompoundTag(byteBuf, DEFAULT_DIMENSION_1_16_2.getTag());
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_16)) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
|
||||
byteBuf.writeByte(previousGamemode);
|
||||
}
|
||||
writeString(byteBuf, levelType);
|
||||
byteBuf.writeLong(partialHashedSeed);
|
||||
writeVarInt(byteBuf, 0); // max players
|
||||
writeVarInt(byteBuf, viewDistance);
|
||||
writeVarInt(byteBuf, simulationDistance);
|
||||
byteBuf.writeBoolean(reducedDebugInfo);
|
||||
byteBuf.writeBoolean(showRespawnScreen);
|
||||
byteBuf.writeBoolean(isDebug);
|
||||
byteBuf.writeBoolean(isFlat);
|
||||
return;
|
||||
}
|
||||
|
||||
if (protocolVersion.inBetween(MINECRAFT_1_19, MINECRAFT_1_19_4)) {
|
||||
byteBuf.writeBoolean(isHardcore);
|
||||
byteBuf.writeByte(gamemode);
|
||||
byteBuf.writeByte(-1); // previous gamemode
|
||||
writeStringArray(byteBuf, levelNames);
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_1) >= 0) {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_4) >= 0) {
|
||||
writeCompoundTag(byteBuf, CODEC_1_19_4);
|
||||
} else {
|
||||
writeCompoundTag(byteBuf, CODEC_1_19_1);
|
||||
}
|
||||
} else {
|
||||
writeCompoundTag(byteBuf, CODEC_1_19);
|
||||
ProtocolUtil.writeStringArray(byteBuf, levelNames);
|
||||
|
||||
final CompoundBinaryTag codec = ProtocolUtil.getCodec(protocolVersion);
|
||||
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
|
||||
ProtocolUtil.writeBinaryTag(byteBuf, protocolVersion, codec);
|
||||
}
|
||||
writeString(byteBuf, worldType);
|
||||
writeString(byteBuf, levelType);
|
||||
byteBuf.writeLong(partialHashedSeed);
|
||||
writeVarInt(byteBuf, 0); // max players
|
||||
writeVarInt(byteBuf, viewDistance);
|
||||
writeVarInt(byteBuf, simulationDistance);
|
||||
byteBuf.writeBoolean(reducedDebugInfo);
|
||||
byteBuf.writeBoolean(showRespawnScreen);
|
||||
byteBuf.writeBoolean(isDebug);
|
||||
byteBuf.writeBoolean(isFlat);
|
||||
byteBuf.writeBoolean(false); // no last death location
|
||||
return;
|
||||
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_16_2)
|
||||
&& protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_19)) {
|
||||
final ListBinaryTag dimensions = codec.getCompound("minecraft:dimension_type").getList("value");
|
||||
final BinaryTag elementTag = ((CompoundBinaryTag) dimensions.get(0)).get("element");
|
||||
ProtocolUtil.writeBinaryTag(byteBuf, protocolVersion, Objects.requireNonNull(elementTag));
|
||||
} else if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
|
||||
ProtocolUtil.writeString(byteBuf, dimension.getKey());
|
||||
}
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
|
||||
ProtocolUtil.writeString(byteBuf, levelName);
|
||||
}
|
||||
} else if (protocolVersion.greaterThan(ProtocolVersion.MINECRAFT_1_9)) {
|
||||
byteBuf.writeInt(dimension.getLegacyId());
|
||||
} else {
|
||||
byteBuf.writeByte(dimension.getLegacyId());
|
||||
}
|
||||
|
||||
if (protocolVersion.equals(MINECRAFT_1_20)) {
|
||||
byteBuf.writeBoolean(isHardcore);
|
||||
byteBuf.writeByte(gamemode);
|
||||
byteBuf.writeByte(-1); // previous gamemode
|
||||
writeStringArray(byteBuf, levelNames);
|
||||
writeCompoundTag(byteBuf, CODEC_1_20);
|
||||
writeString(byteBuf, worldType);
|
||||
writeString(byteBuf, levelType);
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_15)
|
||||
&& protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
|
||||
byteBuf.writeLong(partialHashedSeed);
|
||||
writeVarInt(byteBuf, 0); // max players
|
||||
writeVarInt(byteBuf, viewDistance);
|
||||
writeVarInt(byteBuf, simulationDistance);
|
||||
byteBuf.writeBoolean(reducedDebugInfo);
|
||||
byteBuf.writeBoolean(showRespawnScreen);
|
||||
byteBuf.writeBoolean(isDebug);
|
||||
byteBuf.writeBoolean(isFlat);
|
||||
byteBuf.writeBoolean(false); // no last death location
|
||||
writeVarInt(byteBuf, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (protocolVersion.inBetween(MINECRAFT_1_20_2, MINECRAFT_1_20_3)) {
|
||||
byteBuf.writeBoolean(isHardcore);
|
||||
writeStringArray(byteBuf, levelNames);
|
||||
writeVarInt(byteBuf, 0); // max players
|
||||
writeVarInt(byteBuf, viewDistance);
|
||||
writeVarInt(byteBuf, simulationDistance);
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_14)) {
|
||||
byteBuf.writeByte(difficulty);
|
||||
}
|
||||
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_16_2)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, maxPlayers);
|
||||
} else {
|
||||
byteBuf.writeByte(maxPlayers);
|
||||
}
|
||||
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_16)) {
|
||||
ProtocolUtil.writeString(byteBuf, levelType);
|
||||
}
|
||||
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_14)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, viewDistance);
|
||||
}
|
||||
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_18)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, simulationDistance);
|
||||
}
|
||||
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
byteBuf.writeBoolean(reducedDebugInfo);
|
||||
}
|
||||
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_15)) {
|
||||
byteBuf.writeBoolean(showRespawnScreen);
|
||||
}
|
||||
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_20_2)) {
|
||||
byteBuf.writeBoolean(limitedCrafting);
|
||||
writeString(byteBuf, worldType);
|
||||
writeString(byteBuf, levelType);
|
||||
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_20_5)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, dimension.getId());
|
||||
} else {
|
||||
ProtocolUtil.writeString(byteBuf, dimension.getKey());
|
||||
}
|
||||
|
||||
ProtocolUtil.writeString(byteBuf, levelName);
|
||||
byteBuf.writeLong(partialHashedSeed);
|
||||
byteBuf.writeByte(gamemode);
|
||||
byteBuf.writeByte(-1); // previous gamemode
|
||||
byteBuf.writeBoolean(isDebug);
|
||||
byteBuf.writeBoolean(isFlat);
|
||||
byteBuf.writeBoolean(false); // no last death location
|
||||
writeVarInt(byteBuf, 0);
|
||||
return;
|
||||
byteBuf.writeByte(previousGamemode);
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_20_5) >= 0) {
|
||||
byteBuf.writeBoolean(isHardcore);
|
||||
writeStringArray(byteBuf, levelNames);
|
||||
writeVarInt(byteBuf, 0); // max players
|
||||
writeVarInt(byteBuf, viewDistance);
|
||||
writeVarInt(byteBuf, simulationDistance);
|
||||
byteBuf.writeBoolean(reducedDebugInfo);
|
||||
byteBuf.writeBoolean(showRespawnScreen);
|
||||
byteBuf.writeBoolean(limitedCrafting);
|
||||
// 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, levelType);
|
||||
byteBuf.writeLong(partialHashedSeed);
|
||||
byteBuf.writeByte(gamemode);
|
||||
byteBuf.writeByte(-1); // previous gamemode
|
||||
byteBuf.writeBoolean(isDebug);
|
||||
byteBuf.writeBoolean(isFlat);
|
||||
byteBuf.writeBoolean(false); // no last death location
|
||||
writeVarInt(byteBuf, 0); // pearl cooldown
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_16)) {
|
||||
byteBuf.writeBoolean(debug);
|
||||
byteBuf.writeBoolean(flat);
|
||||
}
|
||||
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_19)) {
|
||||
byteBuf.writeBoolean(false); // last death location
|
||||
}
|
||||
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_20)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, portalCooldown);
|
||||
}
|
||||
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_2)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, seaLevel);
|
||||
}
|
||||
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_20_5)) {
|
||||
byteBuf.writeBoolean(secureProfile);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-11
@@ -25,11 +25,7 @@ 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.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.util.ProtocolUtil.readVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@@ -40,10 +36,10 @@ public final class KeepAlivePacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_12_2) >= 0) {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_12_2)) {
|
||||
byteBuf.writeLong(id);
|
||||
} else if (protocolVersion.compareTo(MINECRAFT_1_8) >= 0) {
|
||||
writeVarInt(byteBuf, (int) id);
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, (int) id);
|
||||
} else {
|
||||
byteBuf.writeInt((int) id);
|
||||
}
|
||||
@@ -51,10 +47,10 @@ public final class KeepAlivePacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void decode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_12_2) >= 0) {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_12_2)) {
|
||||
id = byteBuf.readLong();
|
||||
} else if (protocolVersion.compareTo(MINECRAFT_1_8) >= 0) {
|
||||
id = readVarInt(byteBuf);
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
id = ProtocolUtil.readVarInt(byteBuf);
|
||||
} else {
|
||||
id = byteBuf.readInt();
|
||||
}
|
||||
|
||||
+32
-30
@@ -24,8 +24,7 @@ import lombok.NoArgsConstructor;
|
||||
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;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@@ -33,43 +32,46 @@ import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
|
||||
public final class MapDataPacket implements FallbackPacket {
|
||||
private byte[] buffer;
|
||||
private int x, y;
|
||||
private int scaling;
|
||||
private boolean locked;
|
||||
|
||||
@Override
|
||||
public void encode(final @NotNull ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) {
|
||||
writeVarInt(byteBuf, 0);
|
||||
ProtocolUtil.writeVarInt(byteBuf, 0); // item damage
|
||||
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_8) < 0) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
byteBuf.writeShort(buffer.length + 3);
|
||||
byteBuf.writeByte(0); // scaling
|
||||
byteBuf.writeByte(0);
|
||||
byteBuf.writeByte(x);
|
||||
byteBuf.writeByte(y);
|
||||
byteBuf.writeBytes(buffer);
|
||||
} else {
|
||||
byteBuf.writeByte(0); // scaling
|
||||
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_9) >= 0
|
||||
&& protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_17) < 0) {
|
||||
byteBuf.writeBoolean(false);
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_14) >= 0) {
|
||||
byteBuf.writeBoolean(false);
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_17) >= 0) {
|
||||
byteBuf.writeBoolean(false);
|
||||
} else {
|
||||
writeVarInt(byteBuf, 0);
|
||||
}
|
||||
|
||||
byteBuf.writeByte(128); // rows
|
||||
byteBuf.writeByte(128); // columns
|
||||
byteBuf.writeByte(x);
|
||||
byteBuf.writeByte(y);
|
||||
|
||||
writeVarInt(byteBuf, buffer.length);
|
||||
byteBuf.writeBytes(buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
byteBuf.writeByte(scaling);
|
||||
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_9)
|
||||
&& protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_17)) {
|
||||
byteBuf.writeBoolean(false); // no icon
|
||||
}
|
||||
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_14)) {
|
||||
byteBuf.writeBoolean(locked);
|
||||
}
|
||||
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_17)) {
|
||||
byteBuf.writeBoolean(false); // no icon
|
||||
} else {
|
||||
ProtocolUtil.writeVarInt(byteBuf, 0); // no icon
|
||||
}
|
||||
|
||||
byteBuf.writeByte(128); // rows
|
||||
byteBuf.writeByte(128); // columns
|
||||
byteBuf.writeByte(x);
|
||||
byteBuf.writeByte(y);
|
||||
|
||||
ProtocolUtil.writeVarInt(byteBuf, buffer.length);
|
||||
byteBuf.writeBytes(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+13
-8
@@ -26,33 +26,38 @@ 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.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_8;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class PlayerInputPacket implements FallbackPacket {
|
||||
private float sideways, forward;
|
||||
private boolean jump, unmount;
|
||||
private boolean jump, sneak;
|
||||
|
||||
@Override
|
||||
public void encode(final @NotNull ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
public void encode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(final @NotNull ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
public void decode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_2)) {
|
||||
final byte mask = byteBuf.readByte();
|
||||
jump = (mask & 16) != 0;
|
||||
sneak = (mask & 32) != 0;
|
||||
return;
|
||||
}
|
||||
|
||||
sideways = byteBuf.readFloat();
|
||||
forward = byteBuf.readFloat();
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_8) < 0) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
jump = byteBuf.readBoolean();
|
||||
unmount = byteBuf.readBoolean();
|
||||
sneak = byteBuf.readBoolean();
|
||||
} else {
|
||||
final byte flags = byteBuf.readByte();
|
||||
jump = (flags & 0x01) != 0;
|
||||
unmount = (flags & 0x02) != 0;
|
||||
sneak = (flags & 0x02) != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-9
@@ -23,12 +23,9 @@ 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.util.ProtocolUtil;
|
||||
import xyz.jonesdev.sonar.common.util.exception.QuietDecoderException;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_8;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.readExtendedForgeShort;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.readString;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
public final class PluginMessagePacket implements FallbackPacket {
|
||||
@@ -44,16 +41,16 @@ public final class PluginMessagePacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void decode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
channel = readString(byteBuf, 48);
|
||||
channel = ProtocolUtil.readString(byteBuf, 48);
|
||||
|
||||
final int length;
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_8) >= 0) {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
length = byteBuf.readableBytes();
|
||||
if (length > Short.MAX_VALUE) {
|
||||
throw QuietDecoderException.INSTANCE;
|
||||
}
|
||||
} else {
|
||||
length = readExtendedForgeShort(byteBuf);
|
||||
length = ProtocolUtil.readExtendedForgeShort(byteBuf);
|
||||
if (length > FORGE_MAX_ARRAY_LENGTH) {
|
||||
throw QuietDecoderException.INSTANCE;
|
||||
}
|
||||
@@ -64,12 +61,12 @@ public final class PluginMessagePacket implements FallbackPacket {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int expectedMinLength(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
public int expectedMinLength(final ProtocolVersion protocolVersion) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int expectedMaxLength(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
public int expectedMaxLength(final ProtocolVersion protocolVersion) {
|
||||
return 0xFFF; // strict size limit
|
||||
}
|
||||
}
|
||||
+5
-6
@@ -25,8 +25,7 @@ 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;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@@ -38,14 +37,14 @@ public final class RemoveEntitiesPacket implements FallbackPacket {
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
// No idea why Mojang decided that this is a good idea, but whatever
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_17) != 0) {
|
||||
writeVarInt(byteBuf, 1); // size
|
||||
if (!protocolVersion.equals(ProtocolVersion.MINECRAFT_1_17)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, 1); // size
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_8) < 0) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
byteBuf.writeInt(entityId);
|
||||
} else {
|
||||
writeVarInt(byteBuf, entityId);
|
||||
ProtocolUtil.writeVarInt(byteBuf, entityId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+26
-31
@@ -26,10 +26,8 @@ import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||
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.captcha.ItemType;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.*;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.item.ItemType;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@@ -42,61 +40,58 @@ public final class SetContainerSlotPacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void encode(final @NotNull ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
byteBuf.writeByte(windowId);
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_2)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, windowId);
|
||||
} else {
|
||||
byteBuf.writeByte(windowId);
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_17_1) >= 0) {
|
||||
writeVarInt(byteBuf, 0);
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_17_1)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, 0);
|
||||
}
|
||||
|
||||
byteBuf.writeShort(slot);
|
||||
|
||||
if (protocolVersion.inBetween(MINECRAFT_1_13_2, MINECRAFT_1_20_3)) {
|
||||
if (protocolVersion.inBetween(ProtocolVersion.MINECRAFT_1_13_2, ProtocolVersion.MINECRAFT_1_20_3)) {
|
||||
byteBuf.writeBoolean(true);
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_20_5) >= 0) {
|
||||
writeVarInt(byteBuf, count);
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_20_5)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, count);
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13_2) < 0) {
|
||||
byteBuf.writeShort(itemType.getId(protocolVersion));
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_13_2)) {
|
||||
byteBuf.writeShort(itemType.getId().apply(protocolVersion));
|
||||
} else {
|
||||
writeVarInt(byteBuf, itemType.getId(protocolVersion));
|
||||
ProtocolUtil.writeVarInt(byteBuf, itemType.getId().apply(protocolVersion));
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_20_5) < 0) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_5)) {
|
||||
byteBuf.writeByte(count);
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13) < 0) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_13)) {
|
||||
byteBuf.writeShort(0); // data
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_17) < 0) {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_8) < 0) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_17)) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
byteBuf.writeShort(-1);
|
||||
} else {
|
||||
byteBuf.writeByte(0);
|
||||
}
|
||||
} else if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_5)) {
|
||||
ProtocolUtil.writeBinaryTag(byteBuf, protocolVersion, 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+
|
||||
// TODO: find a way to improve this
|
||||
// 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
|
||||
}
|
||||
ProtocolUtil.writeVarInt(byteBuf, 1); // component count to add
|
||||
ProtocolUtil.writeVarInt(byteBuf, 0); // component count to remove
|
||||
ProtocolUtil.writeVarInt(byteBuf, itemType.getComponents().apply(protocolVersion));
|
||||
ProtocolUtil.writeVarInt(byteBuf, 0); // data
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(final @NotNull ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
public void decode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
+3
-6
@@ -26,9 +26,6 @@ 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.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_14;
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_17;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@@ -38,18 +35,18 @@ public final class SetDefaultSpawnPositionPacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_8) < 0) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
byteBuf.writeInt(x);
|
||||
byteBuf.writeInt(y);
|
||||
byteBuf.writeInt(z);
|
||||
} else {
|
||||
final long encoded = protocolVersion.compareTo(MINECRAFT_1_14) < 0
|
||||
final long encoded = protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_14)
|
||||
? ((x & 0x3FFFFFFL) << 38) | ((y & 0xFFFL) << 26) | (z & 0x3FFFFFFL)
|
||||
: ((x & 0x3FFFFFFL) << 38) | ((y & 0x3FFFFFFL) << 12) | (z & 0xFFFL);
|
||||
|
||||
byteBuf.writeLong(encoded);
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_17) >= 0) {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_17)) {
|
||||
byteBuf.writeFloat(0f);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -25,9 +25,7 @@ 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.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_8;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@@ -41,13 +39,15 @@ public final class SetExperiencePacket implements FallbackPacket {
|
||||
@Override
|
||||
public void encode(final @NotNull ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
byteBuf.writeFloat(experienceBar);
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_8) < 0) {
|
||||
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
byteBuf.writeShort(level);
|
||||
byteBuf.writeShort(totalExperience);
|
||||
} else {
|
||||
writeVarInt(byteBuf, level);
|
||||
writeVarInt(byteBuf, totalExperience);
|
||||
return;
|
||||
}
|
||||
|
||||
ProtocolUtil.writeVarInt(byteBuf, level);
|
||||
ProtocolUtil.writeVarInt(byteBuf, totalExperience);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
-7
@@ -25,9 +25,7 @@ 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.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_9;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@@ -40,16 +38,16 @@ public final class SetPassengersPacket implements FallbackPacket {
|
||||
public void encode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
// You can find this in the EntityAttach packet,
|
||||
// which was later replaced by SetPassengers in 1.9+
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_9) < 0) {
|
||||
if (protocolVersion.lessThanOrEquals(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
byteBuf.writeInt(passengerId);
|
||||
byteBuf.writeInt(entityId);
|
||||
byteBuf.writeByte(0); // leash
|
||||
return;
|
||||
}
|
||||
|
||||
writeVarInt(byteBuf, entityId);
|
||||
writeVarInt(byteBuf, 1); // passenger count
|
||||
writeVarInt(byteBuf, passengerId);
|
||||
ProtocolUtil.writeVarInt(byteBuf, entityId);
|
||||
ProtocolUtil.writeVarInt(byteBuf, 1); // passenger count
|
||||
ProtocolUtil.writeVarInt(byteBuf, passengerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+8
-2
@@ -31,7 +31,7 @@ import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class SetPlayerOnGround implements FallbackPacket {
|
||||
private boolean onGround;
|
||||
private boolean onGround, horizontalCollision;
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
@@ -40,6 +40,12 @@ public final class SetPlayerOnGround implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void decode(final @NotNull ByteBuf byteBuf, final ProtocolVersion protocolVersion) throws Exception {
|
||||
onGround = byteBuf.readBoolean();
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_2)) {
|
||||
final short flag = byteBuf.readUnsignedByte();
|
||||
onGround = (flag & 1) != 0;
|
||||
horizontalCollision = (flag & 2) != 0;
|
||||
} else {
|
||||
onGround = byteBuf.readBoolean();
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-8
@@ -26,15 +26,13 @@ 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.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_8;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class SetPlayerPositionPacket implements FallbackPacket {
|
||||
private double x, y, z;
|
||||
private boolean onGround;
|
||||
private boolean onGround, horizontalCollision;
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
@@ -45,21 +43,27 @@ public final class SetPlayerPositionPacket implements FallbackPacket {
|
||||
public void decode(final @NotNull ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
x = byteBuf.readDouble();
|
||||
y = byteBuf.readDouble();
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_8) < 0) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
// 1.7.2-1.7.10 send the minimum bounding box Y coordinate
|
||||
byteBuf.readDouble();
|
||||
}
|
||||
z = byteBuf.readDouble();
|
||||
onGround = byteBuf.readBoolean();
|
||||
if (protocolVersion.greaterThan(ProtocolVersion.MINECRAFT_1_21_2)) {
|
||||
short flag = byteBuf.readUnsignedByte();
|
||||
onGround = (flag & 1) != 0;
|
||||
horizontalCollision = (flag & 2) != 0;
|
||||
} else {
|
||||
onGround = byteBuf.readBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int expectedMaxLength(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) {
|
||||
return protocolVersion.compareTo(MINECRAFT_1_8) < 0 ? 33 : 25;
|
||||
public int expectedMaxLength(final @NotNull ProtocolVersion protocolVersion) {
|
||||
return protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_8) ? 33 : 25;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int expectedMinLength(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
public int expectedMinLength(final ProtocolVersion protocolVersion) {
|
||||
return 25;
|
||||
}
|
||||
}
|
||||
+39
-18
@@ -25,9 +25,7 @@ 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.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@@ -37,24 +35,41 @@ public final class SetPlayerPositionRotationPacket implements FallbackPacket {
|
||||
private double x, y, z;
|
||||
private float yaw, pitch;
|
||||
private int teleportId, relativeMask;
|
||||
private boolean onGround;
|
||||
private boolean onGround, horizontalCollision;
|
||||
private boolean dismountVehicle;
|
||||
|
||||
@Override
|
||||
public void encode(final @NotNull ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) {
|
||||
final boolean v1_21_2 = protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_2);
|
||||
if (v1_21_2) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, teleportId);
|
||||
}
|
||||
byteBuf.writeDouble(x);
|
||||
// Account for the minimum Y bounding box issue on 1.7.2-1.7.10
|
||||
byteBuf.writeDouble(protocolVersion.compareTo(MINECRAFT_1_8) >= 0 ? y : y + 1.62f);
|
||||
byteBuf.writeDouble(protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_8) ? y : y + 1.62f);
|
||||
byteBuf.writeDouble(z);
|
||||
byteBuf.writeFloat(yaw);
|
||||
byteBuf.writeFloat(pitch);
|
||||
byteBuf.writeByte(relativeMask);
|
||||
if (v1_21_2) {
|
||||
// Delta movement?
|
||||
byteBuf.writeDouble(0);
|
||||
byteBuf.writeDouble(0);
|
||||
byteBuf.writeDouble(0);
|
||||
byteBuf.writeFloat(yaw);
|
||||
byteBuf.writeFloat(pitch);
|
||||
byteBuf.writeInt(relativeMask);
|
||||
} else {
|
||||
byteBuf.writeFloat(yaw);
|
||||
byteBuf.writeFloat(pitch);
|
||||
byteBuf.writeByte(relativeMask);
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_8) > 0) {
|
||||
writeVarInt(byteBuf, teleportId);
|
||||
if (protocolVersion.greaterThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
if (!v1_21_2) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, teleportId);
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_17) >= 0
|
||||
&& protocolVersion.compareTo(MINECRAFT_1_19_3) <= 0) {
|
||||
byteBuf.writeBoolean(true); // Always dismount vehicle
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_17)
|
||||
&& protocolVersion.lessThanOrEquals(ProtocolVersion.MINECRAFT_1_19_3)) {
|
||||
byteBuf.writeBoolean(dismountVehicle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,23 +78,29 @@ public final class SetPlayerPositionRotationPacket implements FallbackPacket {
|
||||
public void decode(final @NotNull ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
x = byteBuf.readDouble();
|
||||
y = byteBuf.readDouble();
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_8) < 0) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
// 1.7.2-1.7.10 send the minimum bounding box Y coordinate
|
||||
byteBuf.readDouble();
|
||||
}
|
||||
z = byteBuf.readDouble();
|
||||
yaw = byteBuf.readFloat();
|
||||
pitch = byteBuf.readFloat();
|
||||
onGround = byteBuf.readBoolean();
|
||||
if (protocolVersion.greaterThan(ProtocolVersion.MINECRAFT_1_21_2)) {
|
||||
short flag = byteBuf.readUnsignedByte();
|
||||
onGround = (flag & 1) != 0;
|
||||
horizontalCollision = (flag & 2) != 0;
|
||||
} else {
|
||||
onGround = byteBuf.readBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int expectedMaxLength(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) {
|
||||
return protocolVersion.compareTo(MINECRAFT_1_8) < 0 ? 41 : 33;
|
||||
public int expectedMaxLength(final @NotNull ProtocolVersion protocolVersion) {
|
||||
return protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_8) ? 41 : 33;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int expectedMinLength(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
public int expectedMinLength(final ProtocolVersion protocolVersion) {
|
||||
return 33;
|
||||
}
|
||||
}
|
||||
+8
-2
@@ -32,7 +32,7 @@ import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
@AllArgsConstructor
|
||||
public final class SetPlayerRotationPacket implements FallbackPacket {
|
||||
private float yaw, pitch;
|
||||
private boolean onGround;
|
||||
private boolean onGround, horizontalCollision;
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
@@ -43,6 +43,12 @@ public final class SetPlayerRotationPacket implements FallbackPacket {
|
||||
public void decode(final @NotNull ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
yaw = byteBuf.readFloat();
|
||||
pitch = byteBuf.readFloat();
|
||||
onGround = byteBuf.readBoolean();
|
||||
if (protocolVersion.greaterThan(ProtocolVersion.MINECRAFT_1_21_2)) {
|
||||
short flag = byteBuf.readUnsignedByte();
|
||||
onGround = (flag & 1) != 0;
|
||||
horizontalCollision = (flag & 2) != 0;
|
||||
} else {
|
||||
onGround = byteBuf.readBoolean();
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-16
@@ -26,13 +26,10 @@ 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.entity.EntityType;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeUUID;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@@ -46,21 +43,19 @@ public final class SpawnEntityPacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
writeVarInt(byteBuf, entityId);
|
||||
ProtocolUtil.writeVarInt(byteBuf, entityId);
|
||||
|
||||
final boolean v1_9orHigher = protocolVersion.compareTo(MINECRAFT_1_9) >= 0;
|
||||
|
||||
if (v1_9orHigher) {
|
||||
writeUUID(byteBuf, UUID.randomUUID());
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_9)) {
|
||||
ProtocolUtil.writeUUID(byteBuf, UUID.randomUUID());
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_14) >= 0) {
|
||||
writeVarInt(byteBuf, entityType.getId(protocolVersion));
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_14)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, entityType.getId().apply(protocolVersion));
|
||||
} else {
|
||||
byteBuf.writeByte(entityType.getId(protocolVersion));
|
||||
byteBuf.writeByte(entityType.getId().apply(protocolVersion));
|
||||
}
|
||||
|
||||
if (v1_9orHigher) {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_9)) {
|
||||
byteBuf.writeDouble(x);
|
||||
byteBuf.writeDouble(y);
|
||||
byteBuf.writeDouble(z);
|
||||
@@ -73,14 +68,14 @@ public final class SpawnEntityPacket implements FallbackPacket {
|
||||
byteBuf.writeByte(0); // pitch or yaw
|
||||
byteBuf.writeByte(0); // yaw or pitch
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19) >= 0) {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_19)) {
|
||||
byteBuf.writeByte(0); // head yaw
|
||||
writeVarInt(byteBuf, data); // data
|
||||
ProtocolUtil.writeVarInt(byteBuf, data); // data
|
||||
} else {
|
||||
byteBuf.writeInt(data); // data
|
||||
}
|
||||
|
||||
if (v1_9orHigher || data > 0) {
|
||||
if (data > 0 || protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_9)) {
|
||||
byteBuf.writeShort((int) (velocityX * 8000D));
|
||||
byteBuf.writeShort((int) (velocityY * 8000D));
|
||||
byteBuf.writeShort((int) (velocityZ * 8000D));
|
||||
|
||||
+23
-25
@@ -25,13 +25,11 @@ 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.util.ComponentHolder;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
import xyz.jonesdev.sonar.common.util.exception.QuietDecoderException;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.*;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@@ -48,38 +46,39 @@ public final class SystemChatPacket implements FallbackPacket {
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
// Serialized message
|
||||
componentHolder.write(byteBuf, protocolVersion);
|
||||
componentHolder.write(byteBuf, protocolVersion, false);
|
||||
|
||||
// Type
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_1) >= 0) {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_19_1)) {
|
||||
byteBuf.writeBoolean(false); // it's not the GAME_INFO type
|
||||
} else if (protocolVersion.compareTo(MINECRAFT_1_19) >= 0) {
|
||||
writeVarInt(byteBuf, 1); // system chat
|
||||
} else if (protocolVersion.compareTo(MINECRAFT_1_8) >= 0) {
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_19)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, 1); // system chat
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
byteBuf.writeByte(1); // system chat
|
||||
}
|
||||
|
||||
// Sender
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16) >= 0
|
||||
&& protocolVersion.compareTo(MINECRAFT_1_19) < 0) {
|
||||
writeUUID(byteBuf, UUID.randomUUID());
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_16)
|
||||
&& protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_19)) {
|
||||
ProtocolUtil.writeUUID(byteBuf, UUID.randomUUID());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
message = readString(byteBuf, 256);
|
||||
message = ProtocolUtil.readString(byteBuf, 256);
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19) >= 0) {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_1) <= 0) {
|
||||
byteBuf.readLong(); // expiresAt
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_19)) {
|
||||
byteBuf.readLong(); // timestamp
|
||||
|
||||
if (protocolVersion.lessThanOrEquals(ProtocolVersion.MINECRAFT_1_19_1)) {
|
||||
final long saltLong = byteBuf.readLong();
|
||||
final byte[] signatureBytes = readByteArray(byteBuf);
|
||||
final byte[] signatureBytes = ProtocolUtil.readByteArray(byteBuf);
|
||||
boolean unsigned = false;
|
||||
|
||||
if (saltLong != 0L && signatureBytes.length > 0) {
|
||||
// No need to store the valid signature
|
||||
} else if ((protocolVersion.compareTo(MINECRAFT_1_19_1) >= 0
|
||||
} else if ((protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_19_1)
|
||||
|| saltLong == 0L) && signatureBytes.length == 0) {
|
||||
unsigned = true;
|
||||
} else {
|
||||
@@ -91,24 +90,23 @@ public final class SystemChatPacket implements FallbackPacket {
|
||||
throw QuietDecoderException.INSTANCE;
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_19_1) >= 0) {
|
||||
final int size = readVarInt(byteBuf);
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_19_1)) {
|
||||
final int size = ProtocolUtil.readVarInt(byteBuf);
|
||||
if (size < 0 || size > 5) {
|
||||
throw QuietDecoderException.INSTANCE;
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
readUUID(byteBuf);
|
||||
readByteArray(byteBuf);
|
||||
ProtocolUtil.readUUID(byteBuf);
|
||||
ProtocolUtil.readByteArray(byteBuf);
|
||||
}
|
||||
|
||||
if (byteBuf.readBoolean()) {
|
||||
readUUID(byteBuf);
|
||||
readByteArray(byteBuf);
|
||||
ProtocolUtil.readUUID(byteBuf);
|
||||
ProtocolUtil.readByteArray(byteBuf);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
byteBuf.readLong(); // timestamp
|
||||
byteBuf.readLong(); // salt
|
||||
final boolean signed = byteBuf.readBoolean();
|
||||
|
||||
@@ -117,7 +115,7 @@ public final class SystemChatPacket implements FallbackPacket {
|
||||
byteBuf.readBytes(sign);
|
||||
}
|
||||
|
||||
readVarInt(byteBuf);
|
||||
ProtocolUtil.readVarInt(byteBuf);
|
||||
final byte[] bytes = new byte[DIV_FLOOR];
|
||||
byteBuf.readBytes(bytes);
|
||||
}
|
||||
|
||||
+5
-8
@@ -25,10 +25,7 @@ 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.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_8;
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_9;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@@ -41,13 +38,13 @@ public final class TeleportEntityPacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void encode(final @NotNull ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_8) >= 0) {
|
||||
writeVarInt(byteBuf, entityId);
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, entityId);
|
||||
} else {
|
||||
byteBuf.writeInt(entityId);
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_9) >= 0) {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_9)) {
|
||||
byteBuf.writeDouble(x);
|
||||
byteBuf.writeDouble(y);
|
||||
byteBuf.writeDouble(z);
|
||||
@@ -60,7 +57,7 @@ public final class TeleportEntityPacket implements FallbackPacket {
|
||||
byteBuf.writeByte(0); // pitch or yaw
|
||||
byteBuf.writeByte(0); // yaw or pitch
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_8) >= 0) {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
byteBuf.writeBoolean(onGround);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-4
@@ -26,8 +26,6 @@ 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.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_17;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@@ -38,7 +36,7 @@ public final class TransactionPacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void decode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_17) < 0) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_17)) {
|
||||
windowId = byteBuf.readByte();
|
||||
transactionId = byteBuf.readShort();
|
||||
accepted = byteBuf.readBoolean();
|
||||
@@ -52,7 +50,7 @@ public final class TransactionPacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_17) < 0) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_17)) {
|
||||
byteBuf.writeByte(windowId);
|
||||
byteBuf.writeShort((short) transactionId);
|
||||
byteBuf.writeBoolean(accepted);
|
||||
|
||||
+3
-5
@@ -25,9 +25,7 @@ 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.writeString;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@@ -39,8 +37,8 @@ public final class TransferPacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
writeString(byteBuf, destinationHost);
|
||||
writeVarInt(byteBuf, destinationPort);
|
||||
ProtocolUtil.writeString(byteBuf, destinationHost);
|
||||
ProtocolUtil.writeVarInt(byteBuf, destinationPort);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+11
-14
@@ -26,10 +26,7 @@ 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.BlockUpdate;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarInt;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeVarLong;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@@ -41,28 +38,28 @@ public final class UpdateSectionBlocksPacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) throws Exception {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_16_2) < 0) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_16_2)) {
|
||||
byteBuf.writeInt(sectionX);
|
||||
byteBuf.writeInt(sectionZ);
|
||||
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_8) < 0) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
byteBuf.writeShort(blockUpdates.length);
|
||||
byteBuf.writeInt(4 * blockUpdates.length);
|
||||
} else {
|
||||
writeVarInt(byteBuf, blockUpdates.length);
|
||||
ProtocolUtil.writeVarInt(byteBuf, blockUpdates.length);
|
||||
}
|
||||
|
||||
for (final BlockUpdate block : blockUpdates) {
|
||||
byteBuf.writeShort(block.getLegacyBlockState());
|
||||
final int blockId = block.getBlockType().getId().apply(protocolVersion);
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_13) >= 0) {
|
||||
writeVarInt(byteBuf, blockId);
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_13)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, blockId);
|
||||
} else {
|
||||
final int shiftedBlockId = blockId << 4;
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_8) < 0) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
byteBuf.writeShort(shiftedBlockId);
|
||||
} else {
|
||||
writeVarInt(byteBuf, shiftedBlockId);
|
||||
ProtocolUtil.writeVarInt(byteBuf, shiftedBlockId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,17 +71,17 @@ public final class UpdateSectionBlocksPacket implements FallbackPacket {
|
||||
byteBuf.writeLong(((sectionX & 0x3FFFFFL) << 42) | (sectionY & 0xFFFFF) | ((sectionZ & 0x3FFFFFL) << 20));
|
||||
|
||||
// 1.20+ don't have light update suppression
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_20) < 0) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20)) {
|
||||
byteBuf.writeBoolean(true); // suppress light updates
|
||||
}
|
||||
|
||||
writeVarInt(byteBuf, blockUpdates.length);
|
||||
ProtocolUtil.writeVarInt(byteBuf, blockUpdates.length);
|
||||
|
||||
for (final BlockUpdate block : blockUpdates) {
|
||||
// https://wiki.vg/Protocol#Update_Section_Blocks
|
||||
final int shiftedBlockId = block.getBlockType().getId().apply(protocolVersion) << 12;
|
||||
final long positionIdValue = shiftedBlockId | block.getBlockState();
|
||||
writeVarLong(byteBuf, positionIdValue);
|
||||
ProtocolUtil.writeVarLong(byteBuf, positionIdValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 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 org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class UpdateTimePacket implements FallbackPacket {
|
||||
private long worldAge, timeOfDay;
|
||||
private boolean tickDayTime;
|
||||
|
||||
@Override
|
||||
public void encode(final @NotNull ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) {
|
||||
byteBuf.writeLong(worldAge);
|
||||
byteBuf.writeLong(timeOfDay);
|
||||
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_2)) {
|
||||
byteBuf.writeBoolean(tickDayTime);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -26,21 +26,21 @@ import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.ratelimit.Ratelimiter;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.time.Duration;
|
||||
|
||||
// Idea taken from Velocity
|
||||
@Setter
|
||||
@RequiredArgsConstructor
|
||||
public final class CaffeineCacheRatelimiter implements Ratelimiter<InetAddress> {
|
||||
private Cache<InetAddress, Long> expiringCache;
|
||||
private long timeout;
|
||||
private Cache<InetAddress, Long> expiringCache;
|
||||
|
||||
public CaffeineCacheRatelimiter(final long timeout, final @NotNull TimeUnit unit) {
|
||||
public CaffeineCacheRatelimiter(final @NotNull Duration duration) {
|
||||
this.timeout = duration.toNanos();
|
||||
this.expiringCache = Caffeine.newBuilder()
|
||||
.ticker(Ticker.systemTicker())
|
||||
.expireAfterWrite(timeout, unit)
|
||||
.expireAfterWrite(duration)
|
||||
.build();
|
||||
this.timeout = unit.toNanos(timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+6
-9
@@ -20,11 +20,9 @@ package xyz.jonesdev.sonar.common.fallback.verification;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.CaptchaPreparer;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.captcha.CaptchaPreparer;
|
||||
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.packets.play.SetContainerSlotPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.map.MapCaptchaInfo;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.play.SetPlayerPositionPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.play.SetPlayerPositionRotationPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.play.SystemChatPacket;
|
||||
@@ -45,10 +43,9 @@ public final class FallbackCaptchaHandler extends FallbackVerificationHandler {
|
||||
|
||||
this.tries = Sonar.get().getConfig().getVerification().getMap().getMaxTries();
|
||||
|
||||
// If the player is on Java, set the 5th slot (ID 4) in the player's hotbar to the map
|
||||
// If the player is on Bedrock, set the 1st slot (ID 0) in the player's hotbar to the map
|
||||
final int slotId = user.isGeyser() ? 36 : 40;
|
||||
user.delayedWrite(new SetContainerSlotPacket(0, slotId, 1, ItemType.FILLED_MAP, MAP_ITEM_NBT));
|
||||
// We may not be on slot 4, so we need to make sure to synchronize the current slot
|
||||
user.delayedWrite(CAPTCHA_HELD_ITEM_SLOT);
|
||||
user.delayedWrite(CAPTCHA_SET_CONTAINER_SLOT);
|
||||
// Send random captcha to the player
|
||||
final MapCaptchaInfo captcha = CaptchaPreparer.getRandomCaptcha();
|
||||
this.answer = captcha.getAnswer().toLowerCase();
|
||||
@@ -99,7 +96,7 @@ public final class FallbackCaptchaHandler extends FallbackVerificationHandler {
|
||||
if (keepAliveStreak++ > 20) {
|
||||
keepAliveStreak = 0;
|
||||
// Send a KeepAlive packet to prevent timeout
|
||||
user.write(CAPTCHA_KEEP_ALIVE);
|
||||
user.write(RANDOM_KEEP_ALIVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+47
-26
@@ -20,12 +20,12 @@ package xyz.jonesdev.sonar.common.fallback.verification;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
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.packets.play.*;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.config.SonarConfiguration.Verification.Gamemode.CREATIVE;
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.FallbackPreparer.*;
|
||||
|
||||
public final class FallbackGravityHandler extends FallbackVerificationHandler {
|
||||
@@ -49,11 +49,11 @@ public final class FallbackGravityHandler extends FallbackVerificationHandler {
|
||||
user.delayedWrite(DEFAULT_ABILITIES);
|
||||
}
|
||||
// Write the DefaultSpawnPosition packet to the buffer
|
||||
if (user.getProtocolVersion().compareTo(MINECRAFT_1_19_3) >= 0) {
|
||||
if (user.getProtocolVersion().greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_19_3)) {
|
||||
user.delayedWrite(defaultSpawnPosition);
|
||||
}
|
||||
// Teleport the player to the position where we're starting to check them
|
||||
if (user.getProtocolVersion().compareTo(MINECRAFT_1_8) >= 0) {
|
||||
if (user.getProtocolVersion().greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
user.delayedWrite(spawnPosition);
|
||||
user.delayedWrite(fallStartPosition);
|
||||
} else {
|
||||
@@ -62,7 +62,7 @@ public final class FallbackGravityHandler extends FallbackVerificationHandler {
|
||||
}
|
||||
// 1.20.3+ introduced game events
|
||||
// Make sure the client knows that we're sending chunks next
|
||||
if (user.getProtocolVersion().compareTo(MINECRAFT_1_20_3) >= 0) {
|
||||
if (user.getProtocolVersion().greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_20_3)) {
|
||||
user.delayedWrite(START_WRITING_CHUNKS);
|
||||
}
|
||||
// Teleport player into an empty world by sending an empty chunk packet
|
||||
@@ -73,11 +73,15 @@ public final class FallbackGravityHandler extends FallbackVerificationHandler {
|
||||
blockHeight = POSSIBLE_BLOCK_TYPES[index].getBlockHeight().apply(user.getProtocolVersion());
|
||||
user.delayedWrite(BLOCKS_PACKETS[index]);
|
||||
}
|
||||
// Update the world time, if necessary
|
||||
if (updateTime != null) {
|
||||
user.delayedWrite(updateTime);
|
||||
}
|
||||
// Send all packets at once
|
||||
user.channel().flush();
|
||||
|
||||
// 1.8 and below don't have TeleportConfirm packets, which is why we're skipping that check.
|
||||
if (user.getProtocolVersion().compareTo(MINECRAFT_1_9) < 0) {
|
||||
if (user.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_9)) {
|
||||
// Enable the movement checks
|
||||
teleported = true;
|
||||
}
|
||||
@@ -87,27 +91,18 @@ public final class FallbackGravityHandler extends FallbackVerificationHandler {
|
||||
private final boolean enableGravityCheck, enableCollisionsCheck;
|
||||
private boolean teleported, canFall, checkMovement;
|
||||
private double y, deltaY, blockHeight;
|
||||
private int movementTick, expectedTeleportId = FIRST_TELEPORT_ID;
|
||||
|
||||
private void markSuccess() {
|
||||
// Force-stop the movement checks
|
||||
teleported = false;
|
||||
// Exempt pre-1.20.2 since they've already passed that check in the configuration phase
|
||||
if (user.getProtocolVersion().compareTo(MINECRAFT_1_20_2) < 0) {
|
||||
preJoinHandler.validateClientInformation();
|
||||
}
|
||||
// Send the player to the next verification handler
|
||||
final var decoder = user.channel().pipeline().get(FallbackPacketDecoder.class);
|
||||
decoder.setListener(new FallbackProtocolHandler(user));
|
||||
}
|
||||
private int movementTick, clientTick, expectedTeleportId = FIRST_TELEPORT_ID;
|
||||
private SetPlayerPositionRotationPacket lastPositionPacket;
|
||||
|
||||
@Override
|
||||
public void handle(final @NotNull FallbackPacket packet) {
|
||||
if (packet instanceof SetPlayerPositionRotationPacket) {
|
||||
// Make sure the player has teleported before checking for position packets
|
||||
final SetPlayerPositionRotationPacket position = (SetPlayerPositionRotationPacket) packet;
|
||||
if (teleported) {
|
||||
final SetPlayerPositionRotationPacket position = (SetPlayerPositionRotationPacket) packet;
|
||||
handleMovement(position.getX(), position.getY(), position.getZ(), position.isOnGround(), true);
|
||||
} else if (user.getProtocolVersion().greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_2)) {
|
||||
lastPositionPacket = position;
|
||||
}
|
||||
} else if (packet instanceof SetPlayerPositionPacket) {
|
||||
// Make sure the player has teleported before checking for position packets
|
||||
@@ -116,29 +111,50 @@ public final class FallbackGravityHandler extends FallbackVerificationHandler {
|
||||
handleMovement(position.getX(), position.getY(), position.getZ(), position.isOnGround(), false);
|
||||
}
|
||||
} else if (packet instanceof ConfirmTeleportationPacket) {
|
||||
final ConfirmTeleportationPacket teleportConfirm = (ConfirmTeleportationPacket) packet;
|
||||
final ConfirmTeleportationPacket confirmTeleport = (ConfirmTeleportationPacket) packet;
|
||||
|
||||
// Only expect this packet to be sent once
|
||||
checkState(!teleported, "duplicate teleport confirm");
|
||||
// Check if the teleport ID matches the expected ID
|
||||
final int teleportId = teleportConfirm.getTeleportId();
|
||||
checkState(teleportId == expectedTeleportId,
|
||||
"expected TP ID " + expectedTeleportId + ", but got " + teleportId);
|
||||
checkState(confirmTeleport.getTeleportId() == expectedTeleportId,
|
||||
"expected TP ID " + expectedTeleportId + ", but got " + confirmTeleport.getTeleportId());
|
||||
|
||||
// The first teleport ID is not useful for us in this context, skip it
|
||||
if (expectedTeleportId == FIRST_TELEPORT_ID) {
|
||||
lastPositionPacket = null;
|
||||
expectedTeleportId = SECOND_TELEPORT_ID;
|
||||
} else {
|
||||
// Enable the movement checks
|
||||
teleported = true;
|
||||
|
||||
if (user.getProtocolVersion().greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_2)) {
|
||||
checkState(lastPositionPacket != null, "excepted position rotation but got teleport confirm.");
|
||||
handleMovement(
|
||||
lastPositionPacket.getX(), lastPositionPacket.getY(), lastPositionPacket.getZ(),
|
||||
lastPositionPacket.isOnGround(), true);
|
||||
}
|
||||
}
|
||||
} else if (packet instanceof ClientInformationPacket
|
||||
|| packet instanceof PluginMessagePacket) {
|
||||
// Pass these packets back to the login handler
|
||||
// TODO: recode this
|
||||
preJoinHandler.handle(packet);
|
||||
} else if (packet instanceof ClientTickEndPacket) {
|
||||
clientTick++;
|
||||
}
|
||||
}
|
||||
|
||||
private void markSuccess() {
|
||||
// Force-stop the movement checks
|
||||
teleported = false;
|
||||
// Exempt pre-1.20.2 since they've already passed that check in the configuration phase
|
||||
if (!user.isGeyser() && user.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
|
||||
preJoinHandler.validateClientInformation();
|
||||
}
|
||||
// Send the player to the next verification handler
|
||||
user.channel().pipeline().get(FallbackPacketDecoder.class).setListener(new FallbackProtocolHandler(user));
|
||||
}
|
||||
|
||||
private void handleMovement(final double x, final double y, final double z,
|
||||
final boolean onGround, final boolean rotated) {
|
||||
if (!checkMovement) {
|
||||
@@ -178,7 +194,7 @@ public final class FallbackGravityHandler extends FallbackVerificationHandler {
|
||||
}
|
||||
|
||||
// Ensure that the player's Y coordinate is above the collision platform
|
||||
checkState(y >= DEFAULT_Y_COLLIDE_POSITION,
|
||||
checkState(y >= PLATFORM_Y_POSITION,
|
||||
"fell through blocks: " + y + "/" + deltaY + "/" + movementTick);
|
||||
|
||||
// The player is not allowed to move away from the collision platform.
|
||||
@@ -186,6 +202,11 @@ public final class FallbackGravityHandler extends FallbackVerificationHandler {
|
||||
checkState(Math.abs(Math.abs(x) - BLOCKS_PER_ROW) < BLOCKS_PER_ROW, "illegal x offset: " + x);
|
||||
checkState(Math.abs(Math.abs(z) - BLOCKS_PER_ROW) < BLOCKS_PER_ROW, "illegal z offset: " + z);
|
||||
|
||||
// Check if the client is ticking correctly
|
||||
if (user.getProtocolVersion().greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_2)) {
|
||||
checkState(clientTick >= movementTick, "invalid ticking: " + clientTick + "/" + movementTick);
|
||||
}
|
||||
|
||||
if (!onGround) {
|
||||
// The deltaY is 0 whenever the player sends their first position packet.
|
||||
// We have to account for this or the player will falsely fail the verification.
|
||||
@@ -193,7 +214,7 @@ public final class FallbackGravityHandler extends FallbackVerificationHandler {
|
||||
checkState(rotated, "illegal movement packet order: " + deltaY);
|
||||
checkState(movementTick == 0, "illegal y motion: " + movementTick);
|
||||
// 1.7 clients immediately start falling after this packet
|
||||
if (user.getProtocolVersion().compareTo(MINECRAFT_1_8) < 0) {
|
||||
if (user.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
movementTick++;
|
||||
}
|
||||
// We've received the first position packet; the player will now start falling
|
||||
@@ -229,7 +250,7 @@ public final class FallbackGravityHandler extends FallbackVerificationHandler {
|
||||
failOrShowCaptcha("illegal collision tick: " + movementTick + "/" + blockHeight);
|
||||
}
|
||||
// Calculate the difference between the player's Y coordinate and the expected Y coordinate
|
||||
final double collisionOffsetY = (DEFAULT_Y_COLLIDE_POSITION + blockHeight) - y;
|
||||
final double collisionOffsetY = (PLATFORM_Y_POSITION + blockHeight) - y;
|
||||
// Make sure the player is actually colliding with the blocks and not only spoofing ground
|
||||
if (collisionOffsetY != 0) {
|
||||
failOrShowCaptcha("illegal collision: " + collisionOffsetY + "/" + y + "/" + blockHeight);
|
||||
|
||||
+65
-68
@@ -20,6 +20,7 @@ package xyz.jonesdev.sonar.common.fallback.verification;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
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;
|
||||
@@ -34,10 +35,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.FallbackPreparer.*;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.BRAND_CHANNEL;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.BRAND_CHANNEL_LEGACY;
|
||||
|
||||
public final class FallbackPreJoinHandler extends FallbackVerificationHandler {
|
||||
|
||||
@@ -45,55 +43,24 @@ public final class FallbackPreJoinHandler extends FallbackVerificationHandler {
|
||||
super(user);
|
||||
|
||||
// Start initializing the actual join process for pre-1.20.2 clients
|
||||
if (user.getProtocolVersion().compareTo(MINECRAFT_1_20_2) < 0) {
|
||||
if (user.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
|
||||
// This trick helps in reducing unnecessary outgoing server traffic
|
||||
// by avoiding sending other packets to clients that are potentially bots.
|
||||
if (user.getProtocolVersion().compareTo(MINECRAFT_1_8) < 0) {
|
||||
if (user.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
user.channel().eventLoop().schedule(this::markSuccess, 100L, TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
sendKeepAlive();
|
||||
/*
|
||||
* The purpose of this KeepAlive packet is to confirm that the connection
|
||||
* is active and legitimate, thereby preventing bot connections that
|
||||
* could flood the server with login attempts and other unwanted traffic.
|
||||
*/
|
||||
user.write(PRE_JOIN_KEEP_ALIVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean receivedClientInfo, receivedClientBrand, acknowledgedLogin;
|
||||
private int expectedKeepAliveId;
|
||||
|
||||
/**
|
||||
* The purpose of these KeepAlive packets is to confirm that the connection
|
||||
* is active and legitimate, thereby preventing bot connections that
|
||||
* could flood the server with login attempts and other unwanted traffic.
|
||||
*/
|
||||
private void sendKeepAlive() {
|
||||
// Send a KeepAlive packet with a random ID
|
||||
expectedKeepAliveId = RANDOM.nextInt();
|
||||
user.write(new KeepAlivePacket(expectedKeepAliveId));
|
||||
}
|
||||
|
||||
private void markAcknowledged() {
|
||||
acknowledgedLogin = true;
|
||||
// Update state, so we're able to send/receive configuration packets
|
||||
updateEncoderDecoderState(FallbackPacketRegistry.CONFIG);
|
||||
synchronizeClientRegistry();
|
||||
// Write the FinishConfiguration packet to the buffer
|
||||
user.delayedWrite(FINISH_CONFIGURATION);
|
||||
// Send all packets in one flush
|
||||
user.channel().flush();
|
||||
}
|
||||
|
||||
private void markSuccess() {
|
||||
// Pass the player to the next verification handler
|
||||
final var decoder = user.channel().pipeline().get(FallbackPacketDecoder.class);
|
||||
decoder.setListener(new FallbackGravityHandler(user, this));
|
||||
}
|
||||
|
||||
void validateClientInformation() {
|
||||
checkState(receivedClientInfo, "didn't send client settings");
|
||||
// Don't check Geyser players for plugin messages as they don't have them
|
||||
if (!user.isGeyser()) {
|
||||
checkState(receivedClientBrand, "didn't send plugin message");
|
||||
}
|
||||
}
|
||||
private int expectedKeepAliveId = PRE_JOIN_KEEP_ALIVE_ID;
|
||||
|
||||
@Override
|
||||
public void handle(final @NotNull FallbackPacket packet) {
|
||||
@@ -106,26 +73,39 @@ public final class FallbackPreJoinHandler extends FallbackVerificationHandler {
|
||||
checkState(keepAliveId == expectedKeepAliveId,
|
||||
"expected K ID " + expectedKeepAliveId + " but got " + keepAliveId);
|
||||
|
||||
// Immediately verify the player if they do not need any configuration (pre-1.20.2)
|
||||
if (expectedKeepAliveId != 0) {
|
||||
if (user.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
|
||||
markSuccess();
|
||||
} else {
|
||||
markAcknowledged();
|
||||
}
|
||||
}
|
||||
|
||||
// 1.8 clients send KeepAlive packets with the ID 0 every second
|
||||
// while the player is in the "Downloading terrain" screen.
|
||||
expectedKeepAliveId = 0;
|
||||
|
||||
// Immediately verify the player if they do not need any configuration (pre-1.20.2)
|
||||
if (user.getProtocolVersion().compareTo(MINECRAFT_1_20_2) < 0) {
|
||||
markSuccess();
|
||||
}
|
||||
} else if (packet instanceof LoginAcknowledgedPacket) {
|
||||
// Prevent users from sending multiple LoginAcknowledged packets
|
||||
checkState(!acknowledgedLogin, "sent duplicate login ack");
|
||||
markAcknowledged();
|
||||
// Update state, so we're able to send/receive packets during the CONFIG state
|
||||
updateEncoderDecoderState(FallbackPacketRegistry.CONFIG);
|
||||
// Perform the KeepAlive check now (config, not pre-config)
|
||||
user.write(PRE_JOIN_KEEP_ALIVE);
|
||||
} else if (packet instanceof FinishConfigurationPacket) {
|
||||
validateClientInformation();
|
||||
// Update the encoder and decoder state because we're currently in the CONFIG state
|
||||
updateEncoderDecoderState(FallbackPacketRegistry.GAME);
|
||||
if (!user.isGeyser()) {
|
||||
validateClientInformation();
|
||||
}
|
||||
markSuccess();
|
||||
} else if (packet instanceof ClientInformationPacket) {
|
||||
final ClientInformationPacket clientInformation = (ClientInformationPacket) packet;
|
||||
|
||||
if (!user.isGeyser()) {
|
||||
checkState(clientInformation.getViewDistance() >= 2,
|
||||
"view distance: " + clientInformation.getViewDistance());
|
||||
}
|
||||
// Ensure that the client locale is correct
|
||||
validateClientLocale(clientInformation.getLocale());
|
||||
// Check if the player sent an unused bit flag in the skin section
|
||||
@@ -137,8 +117,8 @@ public final class FallbackPreJoinHandler extends FallbackVerificationHandler {
|
||||
} else if (packet instanceof PluginMessagePacket) {
|
||||
final PluginMessagePacket pluginMessage = (PluginMessagePacket) packet;
|
||||
|
||||
final boolean usingModernChannel = pluginMessage.getChannel().equals(BRAND_CHANNEL);
|
||||
final boolean usingLegacyChannel = pluginMessage.getChannel().equals(BRAND_CHANNEL_LEGACY);
|
||||
final boolean usingModernChannel = pluginMessage.getChannel().equals("minecraft:brand");
|
||||
final boolean usingLegacyChannel = pluginMessage.getChannel().equals("MC|Brand");
|
||||
|
||||
// Skip this payload if it does not contain client brand information
|
||||
if (!usingModernChannel && !usingLegacyChannel) {
|
||||
@@ -150,7 +130,8 @@ public final class FallbackPreJoinHandler extends FallbackVerificationHandler {
|
||||
// Check if the channel is correct - 1.13 uses the new namespace
|
||||
// system ('minecraft:' + channel) and anything below 1.13 uses
|
||||
// the legacy namespace system ('MC|' + channel).
|
||||
checkState(usingLegacyChannel || user.getProtocolVersion().compareTo(MINECRAFT_1_13) >= 0,
|
||||
checkState(usingLegacyChannel
|
||||
|| user.getProtocolVersion().greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_13),
|
||||
"illegal PluginMessage channel: " + pluginMessage.getChannel());
|
||||
|
||||
// Validate the client branding using a regex to filter unwanted characters.
|
||||
@@ -162,26 +143,42 @@ public final class FallbackPreJoinHandler extends FallbackVerificationHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private void markAcknowledged() {
|
||||
acknowledgedLogin = true;
|
||||
// Write the new RegistrySync packets to the buffer
|
||||
for (final FallbackPacket packet : getRegistryPackets(user.getProtocolVersion())) {
|
||||
user.delayedWrite(packet);
|
||||
}
|
||||
// Write the FinishConfiguration packet to the buffer
|
||||
user.delayedWrite(FINISH_CONFIGURATION);
|
||||
// Send all packets in one flush
|
||||
user.channel().flush();
|
||||
}
|
||||
|
||||
private void markSuccess() {
|
||||
if (user.channel().isActive()) {
|
||||
if (!Sonar.get().getConfig().getVerification().getValidNameRegex().matcher(user.getUsername()).matches()) {
|
||||
user.disconnect(Sonar.get().getConfig().getVerification().getInvalidUsername());
|
||||
return;
|
||||
}
|
||||
|
||||
// Pass the player to the next verification handler
|
||||
final FallbackGravityHandler gravityHandler = new FallbackGravityHandler(user, this);
|
||||
user.channel().pipeline().get(FallbackPacketDecoder.class).setListener(gravityHandler);
|
||||
}
|
||||
}
|
||||
|
||||
void validateClientInformation() {
|
||||
checkState(receivedClientInfo, "didn't send client settings");
|
||||
checkState(receivedClientBrand, "didn't send plugin message");
|
||||
}
|
||||
|
||||
private void updateEncoderDecoderState(final @NotNull FallbackPacketRegistry registry) {
|
||||
// Update the packet registry state in the encoder and decoder pipelines
|
||||
user.channel().pipeline().get(FallbackPacketDecoder.class).updateRegistry(registry);
|
||||
user.channel().pipeline().get(FallbackPacketEncoder.class).updateRegistry(registry);
|
||||
}
|
||||
|
||||
private void synchronizeClientRegistry() {
|
||||
// 1.20.5+ adds new "game bundle features" which overcomplicate all of this...
|
||||
if (user.getProtocolVersion().compareTo(MINECRAFT_1_20_5) >= 0) {
|
||||
// Write the new RegistrySync packets to the buffer
|
||||
for (final FallbackPacket syncPacket : user.getProtocolVersion().compareTo(MINECRAFT_1_21) < 0
|
||||
? REGISTRY_SYNC_1_20_5 : REGISTRY_SYNC_1_21) {
|
||||
user.delayedWrite(syncPacket);
|
||||
}
|
||||
} else {
|
||||
// Write the old RegistrySync packet to the buffer
|
||||
user.delayedWrite(REGISTRY_SYNC_LEGACY);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateClientBrand(final byte @NotNull [] data) {
|
||||
// Check if the client brand is too short. It has to have at least 2 bytes.
|
||||
checkState(data.length > 1, "client brand is too short");
|
||||
@@ -191,7 +188,7 @@ public final class FallbackPreJoinHandler extends FallbackVerificationHandler {
|
||||
// https://discord.com/channels/923308209769426994/1116066363887321199/1256929441053933608
|
||||
String brand = new String(data, StandardCharsets.UTF_8);
|
||||
// Remove the invalid character at the beginning of the client brand
|
||||
if (user.getProtocolVersion().compareTo(MINECRAFT_1_8) >= 0) {
|
||||
if (user.getProtocolVersion().greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
brand = brand.substring(1);
|
||||
}
|
||||
// Check for illegal client brands
|
||||
|
||||
+72
-75
@@ -20,6 +20,7 @@ package xyz.jonesdev.sonar.common.fallback.verification;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
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.packets.play.AnimationPacket;
|
||||
@@ -27,8 +28,7 @@ import xyz.jonesdev.sonar.common.fallback.protocol.packets.play.EntityAnimationP
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.play.SetHeldItemPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.play.TransactionPacket;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_8;
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.FallbackPreparer.CACHED_HELD_ITEM_SLOT;
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.FallbackPreparer.INVALID_HELD_ITEM_SLOT;
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.FallbackPreparer.PLAYER_ENTITY_ID;
|
||||
|
||||
public final class FallbackProtocolHandler extends FallbackVerificationHandler {
|
||||
@@ -40,77 +40,18 @@ public final class FallbackProtocolHandler extends FallbackVerificationHandler {
|
||||
sendTransaction();
|
||||
}
|
||||
|
||||
private boolean waitingSwingArm, waitingSlotConfirm;
|
||||
private boolean waitingSwingArm, waitingSlotConfirm, waitingTransaction;
|
||||
private short expectedTransactionId;
|
||||
private int currentClientSlotId, expectedSlotId = -1;
|
||||
|
||||
/**
|
||||
* Uses Transaction (ping) packets to check for an immediate, legitimate response from the client
|
||||
* <br>
|
||||
* <a href="https://wiki.vg/Protocol#Ping_.28configuration.29">Wiki.vg - Ping (configuration)</a>
|
||||
* <a href="https://wiki.vg/Protocol#Ping_.28play.29">Wiki.vg - Ping (play)</a>
|
||||
*/
|
||||
private void sendTransaction() {
|
||||
// Send a Transaction (Ping) packet with a random ID
|
||||
expectedTransactionId = (short) -RANDOM.nextInt(Short.MAX_VALUE);
|
||||
user.write(new TransactionPacket(0, expectedTransactionId, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses SetHeldItem packets to check for a legitimate response from the client
|
||||
* <br>
|
||||
* <a href="https://wiki.vg/Protocol#Set_Held_Item_.28serverbound.29">Wiki.vg - SetHeldItem (play)</a>
|
||||
*/
|
||||
private void sendSetHeldItem() {
|
||||
// Move the player's slot by 4 (slot limit divided by 2),
|
||||
// and then modulo it by the slot limit (8) to ensure that we don't send invalid slot IDs.
|
||||
expectedSlotId = (currentClientSlotId + 4) % 8;
|
||||
// Send a HeldItemChange packet to the player to see if the player responds
|
||||
user.delayedWrite(CACHED_HELD_ITEM_SLOT);
|
||||
// Send two SetHeldItem packets with the same slot to check if the player responds with the correct slot.
|
||||
// By vanilla protocol, the client does not respond to duplicate SetHeldItem packets.
|
||||
// We can take advantage of this by sending two packets with the same content to check for a valid response.
|
||||
final SetHeldItemPacket heldItemPacket = new SetHeldItemPacket(expectedSlotId);
|
||||
user.delayedWrite(heldItemPacket);
|
||||
user.delayedWrite(heldItemPacket);
|
||||
user.channel().flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses EntityAnimation packets to check for the excepted Animation (SwingArm) from the client.
|
||||
* <br>
|
||||
* <a href="https://wiki.vg/Protocol#Entity_Animation">Wiki.vg - EntityAnimation</a>
|
||||
* <a href="https://wiki.vg/Protocol#Swing_Arm">Wiki.vg - SwingArm</a>
|
||||
*/
|
||||
private void sendArmAnimation() {
|
||||
waitingSwingArm = true;
|
||||
user.write(new EntityAnimationPacket(PLAYER_ENTITY_ID, EntityAnimationPacket.Type.SWING_MAIN_ARM));
|
||||
}
|
||||
|
||||
private void markSuccess() {
|
||||
// Either send the player to the vehicle check,
|
||||
// send the player to the CAPTCHA, or finish the verification.
|
||||
final var decoder = user.channel().pipeline().get(FallbackPacketDecoder.class);
|
||||
// Pass the player to the next best verification handler
|
||||
if (Sonar.get().getConfig().getVerification().getVehicle().isEnabled()) {
|
||||
decoder.setListener(new FallbackVehicleHandler(user));
|
||||
} else if (user.isForceCaptcha() || Sonar.get().getFallback().shouldPerformCaptcha()) {
|
||||
decoder.setListener(new FallbackCaptchaHandler(user));
|
||||
} else {
|
||||
// The player has passed all checks
|
||||
finishVerification();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(final @NotNull FallbackPacket packet) {
|
||||
if (packet instanceof TransactionPacket) {
|
||||
final TransactionPacket transaction = (TransactionPacket) packet;
|
||||
|
||||
// Make sure random transactions aren't counted
|
||||
checkState(expectedTransactionId <= 0, "unexpected transaction");
|
||||
// Make sure we are expecting a transaction packet
|
||||
checkState(waitingTransaction, "unexpected transaction: " + transaction.getTransactionId());
|
||||
// Make sure the window ID is valid
|
||||
checkState(transaction.getWindowId() == 0, "wrong window ID " + transaction.getWindowId());
|
||||
checkState(transaction.getWindowId() == 0, "wrong window: " + transaction.getWindowId());
|
||||
// Make sure the transaction was accepted
|
||||
// This must - by vanilla protocol - always be accepted
|
||||
checkState(transaction.isAccepted(), "didn't accept transaction");
|
||||
@@ -138,7 +79,7 @@ public final class FallbackProtocolHandler extends FallbackVerificationHandler {
|
||||
sendSetHeldItem();
|
||||
}
|
||||
|
||||
expectedTransactionId = 0;
|
||||
waitingTransaction = false;
|
||||
} else if (packet instanceof SetHeldItemPacket) {
|
||||
final SetHeldItemPacket heldItemPacket = (SetHeldItemPacket) packet;
|
||||
|
||||
@@ -146,10 +87,9 @@ public final class FallbackProtocolHandler extends FallbackVerificationHandler {
|
||||
// Also check if the player sent an invalid slot which is impossible by vanilla protocol
|
||||
checkState(slotId >= 0 && slotId <= 8, "slot out of range: " + slotId);
|
||||
// Check if the player sent a duplicate slot packet which is impossible by vanilla protocol
|
||||
checkState(slotId != currentClientSlotId, "invalid slot: " + slotId);
|
||||
checkState(slotId != currentClientSlotId, "duplicate slot: " + slotId);
|
||||
|
||||
// Only continue checking if we're actually expecting a SetHeldItem packet
|
||||
// The player can send a SetHeldItem packet by themselves -> exempt
|
||||
if (expectedSlotId != -1
|
||||
// Check if the slot ID matches the expected slot ID
|
||||
// This can false flag if a player spams these packets, which is why we don't fail for this
|
||||
@@ -165,21 +105,78 @@ public final class FallbackProtocolHandler extends FallbackVerificationHandler {
|
||||
// Make sure we are awaiting an AnimationPacket packet
|
||||
if (waitingSwingArm) {
|
||||
final AnimationPacket animationPacket = (AnimationPacket) packet;
|
||||
checkState(animationPacket.getHand() == AnimationPacket.MAIN_HAND,
|
||||
"invalid hand " + animationPacket.getHand());
|
||||
// Check that the 1.7 client is responding to the correct entity id and animation type.
|
||||
if (user.getProtocolVersion().compareTo(MINECRAFT_1_8) < 0) {
|
||||
// Check entity id is player itself and if the player is sending the correct animation type
|
||||
if (animationPacket.getEntityId() == PLAYER_ENTITY_ID
|
||||
&& animationPacket.getType() == AnimationPacket.LegacyAnimationType.SWING_ARM) {
|
||||
|
||||
if (user.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
// Check if the entity ID is the player itself
|
||||
checkState(animationPacket.getEntityId() == PLAYER_ENTITY_ID,
|
||||
"invalid entity ID " + animationPacket.getEntityId());
|
||||
// Check if the player is sending the correct animation type
|
||||
if (animationPacket.getType() == AnimationPacket.LegacyAnimationType.SWING_ARM) {
|
||||
markSuccess();
|
||||
waitingSwingArm = false;
|
||||
}
|
||||
} else {
|
||||
checkState(animationPacket.getHand() == AnimationPacket.MAIN_HAND,
|
||||
"invalid hand " + animationPacket.getHand());
|
||||
markSuccess();
|
||||
waitingSwingArm = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses Transaction (ping) packets to check for an immediate, legitimate response from the client
|
||||
* <br>
|
||||
* <a href="https://wiki.vg/Protocol#Ping_.28configuration.29">Wiki.vg - Ping (configuration)</a>
|
||||
* <a href="https://wiki.vg/Protocol#Ping_.28play.29">Wiki.vg - Ping (play)</a>
|
||||
*/
|
||||
private void sendTransaction() {
|
||||
// Send a Transaction (Ping) packet with a random ID
|
||||
expectedTransactionId = (short) -RANDOM.nextInt(Short.MAX_VALUE);
|
||||
user.write(new TransactionPacket(0, expectedTransactionId, false));
|
||||
waitingTransaction = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses SetHeldItem packets to check for a legitimate response from the client
|
||||
* <br>
|
||||
* <a href="https://wiki.vg/Protocol#Set_Held_Item_.28serverbound.29">Wiki.vg - SetHeldItem (play)</a>
|
||||
*/
|
||||
private void sendSetHeldItem() {
|
||||
// Send an invalid HeldItemChange packet to the player to see if the player responds at all
|
||||
user.delayedWrite(INVALID_HELD_ITEM_SLOT);
|
||||
// Increment the player's slot by a random slot, and then modulo it by the maximum slot (8)
|
||||
expectedSlotId = (currentClientSlotId + 1 + RANDOM.nextInt(7)) % 8;
|
||||
// Send two SetHeldItem packets with the same slot to check if the player responds with the correct slot.
|
||||
// By vanilla protocol, the client does not respond to duplicate SetHeldItem packets.
|
||||
// We can take advantage of this by sending two packets with the same content to check for a valid response.
|
||||
final SetHeldItemPacket heldItemPacket = new SetHeldItemPacket(expectedSlotId);
|
||||
user.delayedWrite(heldItemPacket);
|
||||
user.delayedWrite(heldItemPacket);
|
||||
user.channel().flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses EntityAnimation packets to check for the excepted Animation (SwingArm) from the client.
|
||||
* <br>
|
||||
* <a href="https://wiki.vg/Protocol#Entity_Animation">Wiki.vg - EntityAnimation</a>
|
||||
* <a href="https://wiki.vg/Protocol#Swing_Arm">Wiki.vg - SwingArm</a>
|
||||
*/
|
||||
private void sendArmAnimation() {
|
||||
user.write(new EntityAnimationPacket(PLAYER_ENTITY_ID, EntityAnimationPacket.Type.SWING_MAIN_ARM));
|
||||
waitingSwingArm = true;
|
||||
}
|
||||
|
||||
private void markSuccess() {
|
||||
// Pass the player to the next best verification handler
|
||||
if (!user.isGeyser() && Sonar.get().getConfig().getVerification().getVehicle().isEnabled()) {
|
||||
user.channel().pipeline().get(FallbackPacketDecoder.class).setListener(new FallbackVehicleHandler(user));
|
||||
} else if (user.isForceCaptcha() || Sonar.get().getFallback().shouldPerformCaptcha()) {
|
||||
user.channel().pipeline().get(FallbackPacketDecoder.class).setListener(new FallbackCaptchaHandler(user));
|
||||
} else {
|
||||
// The player has passed all checks
|
||||
finishVerification();
|
||||
}
|
||||
}
|
||||
}
|
||||
+141
-167
@@ -17,14 +17,15 @@
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.verification;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
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.packets.play.*;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_9;
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.FallbackPreparer.*;
|
||||
|
||||
public final class FallbackVehicleHandler extends FallbackVerificationHandler {
|
||||
@@ -32,191 +33,164 @@ public final class FallbackVehicleHandler extends FallbackVerificationHandler {
|
||||
public FallbackVehicleHandler(final @NotNull FallbackUser user) {
|
||||
super(user);
|
||||
|
||||
// Send the necessary packets to mount the player on the boat vehicle
|
||||
user.delayedWrite(spawnBoatEntity);
|
||||
user.delayedWrite(setPassengers);
|
||||
user.channel().flush();
|
||||
spawnVehicle(State.IN_BOAT);
|
||||
}
|
||||
|
||||
private int expectedTransactionId;
|
||||
private int paddlePackets, inputPackets, positionPackets, rotationPackets, vehicleMovePackets;
|
||||
private boolean expectMovement, inVoid, inMinecart, waitingSpawnMinecart, waitingRemoveMinecart;
|
||||
private double minecartMotion, minecartY = IN_AIR_Y_POSITION;
|
||||
private boolean waitingForStateChange;
|
||||
private State state = State.WAITING;
|
||||
private State nextState;
|
||||
private int expectedKeepAliveId;
|
||||
private int rotations, inputs, paddles, vehicleMoves;
|
||||
private double boatMotion, boatY = IN_AIR_Y_POSITION;
|
||||
|
||||
private void markSuccess() {
|
||||
// Pass the player to the next best verification handler
|
||||
if (user.isForceCaptcha() || Sonar.get().getFallback().shouldPerformCaptcha()) {
|
||||
// Either send the player to the CAPTCHA or finish the verification.
|
||||
final var decoder = user.channel().pipeline().get(FallbackPacketDecoder.class);
|
||||
// Send the player to the CAPTCHA handler
|
||||
decoder.setListener(new FallbackCaptchaHandler(user));
|
||||
} else {
|
||||
// The player has passed all checks
|
||||
finishVerification();
|
||||
}
|
||||
}
|
||||
@RequiredArgsConstructor
|
||||
private enum State {
|
||||
WAITING(false),
|
||||
IN_BOAT(true),
|
||||
IN_AIR_AFTER_BOAT(false),
|
||||
IN_MINECART(true),
|
||||
IN_AIR_AFTER_MINECART(false);
|
||||
|
||||
// We can abuse the entity remove mechanic and check for position packets when the entity dies
|
||||
private void handleMovement(final double y, final boolean onGround) {
|
||||
// The player cannot be on the ground if the Y position is less than 0
|
||||
checkState(!onGround || !inVoid || waitingRemoveMinecart, "invalid ground state: " + y);
|
||||
// Check the Y position of the player
|
||||
final double minimumY = inVoid ? IN_VOID_Y_POSITION : IN_AIR_Y_POSITION;
|
||||
checkState(y <= minimumY, "illegal y position: " + y + "/" + minimumY);
|
||||
|
||||
if (!waitingRemoveMinecart && !waitingSpawnMinecart
|
||||
&& positionPackets++ > Sonar.get().getConfig().getVerification().getVehicle().getMinimumPackets()) {
|
||||
if (user.isGeyser() || inVoid) {
|
||||
// Mark this check as successful if the player sent a few position packets
|
||||
markSuccess();
|
||||
} else if (!inMinecart) {
|
||||
// Continue checking the player by using Minecarts
|
||||
spawnMinecart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRotation() {
|
||||
// Once the player sent enough packets, go to the next stage
|
||||
final int minimumPackets = Sonar.get().getConfig().getVerification().getVehicle().getMinimumPackets();
|
||||
if (paddlePackets > minimumPackets
|
||||
&& inputPackets > minimumPackets
|
||||
&& rotationPackets > minimumPackets
|
||||
&& vehicleMovePackets > minimumPackets) {
|
||||
// If the player is riding a Minecart, teleport the entity to < -100
|
||||
// to see how the player reacts to the invalid position of the entity.
|
||||
if (inMinecart) {
|
||||
user.delayedWrite(teleportMinecart);
|
||||
// We're using transactions to avoid false positives with lag
|
||||
waitingRemoveMinecart = true;
|
||||
expectedTransactionId = (short) -RANDOM.nextInt(Short.MAX_VALUE);
|
||||
user.delayedWrite(new TransactionPacket(0, expectedTransactionId, false));
|
||||
user.channel().flush();
|
||||
} else {
|
||||
// If the player is riding a boat, remove the entity
|
||||
// The next Y coordinate the player will send is going
|
||||
// to be the vehicle spawn position (64 in this case).
|
||||
user.write(removeBoat);
|
||||
}
|
||||
// Listen for next movement packet(s)
|
||||
expectMovement = true;
|
||||
}
|
||||
rotationPackets++;
|
||||
}
|
||||
|
||||
private void spawnMinecart() {
|
||||
expectMovement = false;
|
||||
paddlePackets = inputPackets = positionPackets = rotationPackets = 0;
|
||||
// Use transaction packets to confirm that the entity has spawned
|
||||
waitingSpawnMinecart = true;
|
||||
minecartMotion = 0.03999999910593033D * 2 * -1;
|
||||
expectedTransactionId = (short) -RANDOM.nextInt(Short.MAX_VALUE);
|
||||
user.delayedWrite(new TransactionPacket(0, expectedTransactionId, false));
|
||||
user.delayedWrite(spawnMinecartEntity);
|
||||
user.delayedWrite(setPassengers);
|
||||
user.channel().flush();
|
||||
private final boolean inVehicle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(final @NotNull FallbackPacket packet) {
|
||||
if (packet instanceof TransactionPacket) {
|
||||
final TransactionPacket transaction = (TransactionPacket) packet;
|
||||
// Make sure random transactions aren't counted
|
||||
checkState(expectedTransactionId <= 0, "unexpected transaction");
|
||||
// Make sure the window ID is valid
|
||||
checkState(transaction.getWindowId() == 0, "wrong window ID " + transaction.getWindowId());
|
||||
// Make sure the transaction was accepted
|
||||
// This must - by vanilla protocol - always be accepted
|
||||
checkState(transaction.isAccepted(), "didn't accept transaction");
|
||||
// Also check if the transaction ID matches the expected ID
|
||||
checkState(expectedTransactionId == transaction.getTransactionId(),
|
||||
"expected T ID " + expectedTransactionId + ", but got " + transaction.getTransactionId());
|
||||
if (packet instanceof KeepAlivePacket) {
|
||||
final KeepAlivePacket keepAlivePacket = (KeepAlivePacket) packet;
|
||||
|
||||
// We're "lag compensating" the Minecart to avoid false positives
|
||||
if (waitingSpawnMinecart) {
|
||||
waitingSpawnMinecart = false;
|
||||
inMinecart = true;
|
||||
} else if (waitingRemoveMinecart) {
|
||||
waitingRemoveMinecart = inMinecart = false;
|
||||
inVoid = true;
|
||||
positionPackets = 0;
|
||||
}
|
||||
// Check if we are expecting a KeepAlive packet
|
||||
checkState(nextState != null, "invalid packet timing");
|
||||
// Also check if the KeepAlive ID matches the expected ID
|
||||
checkState(keepAlivePacket.getId() == expectedKeepAliveId,
|
||||
"expected K ID " + expectedKeepAliveId + ", but got " + keepAlivePacket.getId());
|
||||
|
||||
expectedTransactionId = 0;
|
||||
} else if (packet instanceof SetPlayerPositionRotationPacket) {
|
||||
if (expectMovement) {
|
||||
state = nextState;
|
||||
nextState = null;
|
||||
waitingForStateChange = false;
|
||||
} else if (!waitingForStateChange) {
|
||||
if (packet instanceof PaddleBoatPacket) {
|
||||
if (state == State.IN_BOAT) {
|
||||
paddles++;
|
||||
}
|
||||
} else if (packet instanceof VehicleMovePacket) {
|
||||
if (state == State.IN_BOAT) {
|
||||
final VehicleMovePacket vehicleMove = (VehicleMovePacket) packet;
|
||||
// Check the Y position of the vehicle
|
||||
checkState(vehicleMove.getY() <= IN_AIR_Y_POSITION, "bad vehicle y: " + vehicleMove.getY());
|
||||
|
||||
// Check the gravity of the vehicle
|
||||
final double lastBoatMotion = boatMotion;
|
||||
final double lastBoatY = boatY;
|
||||
boatY = vehicleMove.getY();
|
||||
boatMotion = boatY - lastBoatY;
|
||||
final double predicted = lastBoatMotion - 0.03999999910593033D;
|
||||
final double difference = Math.abs(boatMotion - predicted);
|
||||
// Check if the difference between the predicted and actual motion is too large
|
||||
checkState(difference < 1e-7, "bad vehicle gravity: " + predicted + "/" + boatMotion);
|
||||
|
||||
vehicleMoves++;
|
||||
}
|
||||
} else if (packet instanceof SetPlayerRotationPacket) {
|
||||
if (state.inVehicle) {
|
||||
rotations++;
|
||||
|
||||
// 1.21.2+ do not send PlayerInput packets when inside a vehicle.
|
||||
// Handle it after SetPlayerRotationPacket to simulate vanilla behavior.
|
||||
if (user.getProtocolVersion().greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_2)) {
|
||||
handlePlayerInput();
|
||||
}
|
||||
}
|
||||
} else if (packet instanceof PlayerInputPacket) {
|
||||
// 1.21.2+ send PlayerInput packets when the player starts sprinting, sneaking, etc.
|
||||
if (state.inVehicle && user.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_21_2)) {
|
||||
final PlayerInputPacket playerInput = (PlayerInputPacket) packet;
|
||||
|
||||
// Check if the player is sending invalid vehicle speed values
|
||||
final float forward = Math.abs(playerInput.getForward());
|
||||
final float sideways = Math.abs(playerInput.getSideways());
|
||||
final float maxVehicleSpeed = /*user.isGeyser() ? 1 :*/ 0.98f;
|
||||
checkState(forward <= maxVehicleSpeed, "illegal speed (f): " + forward);
|
||||
checkState(sideways <= maxVehicleSpeed, "illegal speed (s): " + sideways);
|
||||
|
||||
handlePlayerInput();
|
||||
}
|
||||
} else if (packet instanceof SetPlayerPositionRotationPacket) {
|
||||
final SetPlayerPositionRotationPacket posRot = (SetPlayerPositionRotationPacket) packet;
|
||||
handleMovement(posRot.getY(), posRot.isOnGround());
|
||||
}
|
||||
} else if (packet instanceof SetPlayerPositionPacket) {
|
||||
if (expectMovement) {
|
||||
} else if (packet instanceof SetPlayerPositionPacket) {
|
||||
final SetPlayerPositionPacket position = (SetPlayerPositionPacket) packet;
|
||||
handleMovement(position.getY(), position.isOnGround());
|
||||
}
|
||||
} else if (packet instanceof SetPlayerRotationPacket) {
|
||||
if (!expectMovement && !user.isGeyser()) {
|
||||
handleRotation();
|
||||
}
|
||||
} else if (packet instanceof PaddleBoatPacket) {
|
||||
checkState(!inMinecart, "invalid packet order (unexpected PaddleBoatPacket)");
|
||||
paddlePackets++;
|
||||
} else if (packet instanceof VehicleMovePacket) {
|
||||
checkState(!inMinecart, "invalid packet order (unexpected VehicleMovePacket)");
|
||||
final VehicleMovePacket vehicleMove = (VehicleMovePacket) packet;
|
||||
if (!user.isGeyser()) {
|
||||
// Check the Y position of the vehicle
|
||||
checkState(vehicleMove.getY() <= IN_AIR_Y_POSITION,
|
||||
"invalid vehicle y position: " + vehicleMove.getY());
|
||||
// Predict the Y motion of the minecart
|
||||
final double lastMinecartMotion = minecartMotion;
|
||||
final double lastMinecartY = minecartY;
|
||||
minecartY = vehicleMove.getY();
|
||||
minecartMotion = minecartY - lastMinecartY;
|
||||
final double predicted = lastMinecartMotion - 0.03999999910593033D;
|
||||
final double difference = Math.abs(minecartMotion - predicted);
|
||||
// Check if the difference between the predicted and actual motion is too large
|
||||
checkState(difference < 1e-7,
|
||||
"invalid vehicle gravity: " + predicted + "/" + minecartMotion + "/" + difference);
|
||||
}
|
||||
vehicleMovePackets++;
|
||||
} else if (packet instanceof PlayerInputPacket) {
|
||||
if (!expectMovement) {
|
||||
final PlayerInputPacket playerInput = (PlayerInputPacket) packet;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the player is sending invalid vehicle speed values
|
||||
final float forward = Math.abs(playerInput.getForward());
|
||||
final float sideways = Math.abs(playerInput.getSideways());
|
||||
final float maxVehicleSpeed = user.isGeyser() ? 1 : 0.98f;
|
||||
checkState(forward <= maxVehicleSpeed, "illegal speed (f): " + forward);
|
||||
checkState(sideways <= maxVehicleSpeed, "illegal speed (s): " + sideways);
|
||||
private void spawnVehicle(final @NotNull State nextState) {
|
||||
user.delayedWrite(nextState == State.IN_BOAT ? SPAWN_BOAT_ENTITY : SPAWN_MINECART_ENTITY);
|
||||
user.delayedWrite(SET_VEHICLE_PASSENGERS);
|
||||
prepareForNextState(nextState);
|
||||
}
|
||||
|
||||
// Only mark this packet as correct if the player is not moving the vehicle
|
||||
if (playerInput.isJump() || playerInput.isUnmount()) {
|
||||
return;
|
||||
}
|
||||
private void prepareForNextState(final @NotNull State nextState) {
|
||||
this.nextState = nextState;
|
||||
waitingForStateChange = true;
|
||||
expectedKeepAliveId = RANDOM.nextInt();
|
||||
rotations = inputs = paddles = vehicleMoves = 0;
|
||||
user.delayedWrite(new KeepAlivePacket(expectedKeepAliveId));
|
||||
user.channel().flush();
|
||||
}
|
||||
|
||||
// Bedrock users do not send SetPlayerPositionRotation and SetPlayerRotation packets
|
||||
// Don't ask me why; Microsoft is doing some *fascinating* things with Bedrock...
|
||||
if (user.isGeyser()) {
|
||||
handleRotation();
|
||||
} else {
|
||||
checkState(rotationPackets >= inputPackets,
|
||||
"illegal packet order; r/i " + rotationPackets + "/" + inputPackets);
|
||||
}
|
||||
private void markSuccess() {
|
||||
// Pass the player to the next best verification handler
|
||||
if (user.isForceCaptcha() || Sonar.get().getFallback().shouldPerformCaptcha()) {
|
||||
user.channel().pipeline().get(FallbackPacketDecoder.class).setListener(new FallbackCaptchaHandler(user));
|
||||
} else {
|
||||
finishVerification();
|
||||
}
|
||||
}
|
||||
|
||||
// 1.8 and below do not have PaddleBoat packets, so we simply exempt them from the PaddleBoat check.
|
||||
// Clients don't send PaddleBoat & VehicleMovePacket packets while riding minecarts.
|
||||
if (user.getProtocolVersion().compareTo(MINECRAFT_1_9) < 0 || inMinecart) {
|
||||
paddlePackets++;
|
||||
vehicleMovePackets++;
|
||||
} else if (!user.isGeyser()) {
|
||||
checkState(paddlePackets >= inputPackets,
|
||||
"illegal packet order; i/p " + inputPackets + "/" + paddlePackets);
|
||||
checkState(vehicleMovePackets >= inputPackets,
|
||||
"illegal packet order; i/v " + inputPackets + "/" + vehicleMovePackets);
|
||||
}
|
||||
inputPackets++;
|
||||
}
|
||||
private void handleMovement(final double y, final boolean isOnGround) {
|
||||
// Make sure we're currently expecting movement
|
||||
if (state.inVehicle || state == State.WAITING) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the ground state and y position are correct
|
||||
checkState(y <= boatY, "invalid y: " + y);
|
||||
checkState(!isOnGround, "invalid ground state: " + y);
|
||||
|
||||
if (state == State.IN_AIR_AFTER_BOAT) {
|
||||
spawnVehicle(State.IN_MINECART);
|
||||
} else {
|
||||
markSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePlayerInput() {
|
||||
// 1.8 and below do not have PaddleBoat packets, so we simply exempt them from the PaddleBoat check.
|
||||
// Clients also don't send PaddleBoat & VehicleMovePacket packets while riding minecarts.
|
||||
if (user.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_9) || state == State.IN_MINECART) {
|
||||
paddles++;
|
||||
vehicleMoves++;
|
||||
}
|
||||
|
||||
// Check for packet order
|
||||
checkState(rotations >= inputs,
|
||||
"illegal packet order; i/r " + inputs + "/" + rotations);
|
||||
checkState(paddles >= inputs,
|
||||
"illegal packet order; i/p " + inputs + "/" + paddles);
|
||||
checkState(vehicleMoves >= inputs,
|
||||
"illegal packet order; i/v " + inputs + "/" + vehicleMoves);
|
||||
|
||||
inputs++;
|
||||
|
||||
// Check if we've received more than the minimum number of packets
|
||||
final int minimumPackets = Sonar.get().getConfig().getVerification().getVehicle().getMinimumPackets();
|
||||
if (inputs > minimumPackets && rotations > minimumPackets
|
||||
&& paddles > minimumPackets && vehicleMoves > minimumPackets) {
|
||||
// Move on to the next stage
|
||||
user.delayedWrite(REMOVE_VEHICLE);
|
||||
prepareForNextState(state == State.IN_BOAT ? State.IN_AIR_AFTER_BOAT : State.IN_AIR_AFTER_MINECART);
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-6
@@ -25,19 +25,19 @@ import xyz.jonesdev.sonar.api.event.impl.UserBlacklistedEvent;
|
||||
import xyz.jonesdev.sonar.api.event.impl.UserVerifyFailedEvent;
|
||||
import xyz.jonesdev.sonar.api.event.impl.UserVerifySuccessEvent;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.netty.FallbackVarInt21FrameDecoder;
|
||||
import xyz.jonesdev.sonar.common.fallback.netty.FallbackVarIntLengthEncoder;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketDecoder;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketEncoder;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketListener;
|
||||
import xyz.jonesdev.sonar.common.statistics.GlobalSonarStatistics;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
import xyz.jonesdev.sonar.common.util.exception.QuietDecoderException;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_20_5;
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.FallbackPreparer.transferToOrigin;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.closeWith;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public abstract class FallbackVerificationHandler implements FallbackPacketListener {
|
||||
@@ -53,12 +53,11 @@ public abstract class FallbackVerificationHandler implements FallbackPacketListe
|
||||
user.getFingerprint(), user.getLoginTimer().getStart()));
|
||||
|
||||
// Call the VerifySuccessEvent for external API usage
|
||||
Sonar.get().getEventManager().publish(new UserVerifySuccessEvent(user, user.getLoginTimer()));
|
||||
Sonar.get().getEventManager().publish(new UserVerifySuccessEvent(user));
|
||||
|
||||
// If enabled, transfer the player back to the origin server.
|
||||
// This feature was introduced by Mojang in Minecraft version 1.20.5.
|
||||
if (transferToOrigin != null
|
||||
&& user.getProtocolVersion().compareTo(MINECRAFT_1_20_5) >= 0) {
|
||||
if (transferToOrigin != null && user.getProtocolVersion().greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_20_5)) {
|
||||
// Send the transfer packet to the player (and close the channel if on Java Edition)
|
||||
if (user.isGeyser()) {
|
||||
user.write(transferToOrigin);
|
||||
@@ -68,7 +67,7 @@ public abstract class FallbackVerificationHandler implements FallbackPacketListe
|
||||
user.channel().pipeline().remove(FallbackVarInt21FrameDecoder.class);
|
||||
user.channel().pipeline().remove(FallbackVarIntLengthEncoder.class);
|
||||
} else {
|
||||
closeWith(user.channel(), user.getProtocolVersion(), transferToOrigin);
|
||||
ProtocolUtil.closeWith(user.channel(), user.getProtocolVersion(), transferToOrigin);
|
||||
}
|
||||
} else {
|
||||
// Disconnect player with the verification success message
|
||||
|
||||
+1
-1
@@ -130,7 +130,7 @@ public final class GlobalSonarStatistics implements SonarStatistics {
|
||||
|
||||
@Override
|
||||
public int getTotalPlayersVerified() {
|
||||
return Sonar.get().getVerifiedPlayerController().estimatedSize();
|
||||
return Sonar.get().getVerifiedPlayerController().getCache().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,7 +21,7 @@ import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.command.CommandInvocation;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.Subcommand;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.SubcommandInfo;
|
||||
|
||||
@@ -32,20 +32,20 @@ import xyz.jonesdev.sonar.api.command.subcommand.SubcommandInfo;
|
||||
public final class BlacklistCommand extends Subcommand {
|
||||
|
||||
@Override
|
||||
protected void execute(final @NotNull CommandInvocation invocation) {
|
||||
switch (invocation.getRawArguments()[1].toLowerCase()) {
|
||||
protected void execute(final @NotNull InvocationSource source, final String @NotNull [] args) {
|
||||
switch (args[1].toLowerCase()) {
|
||||
case "add": {
|
||||
if (invocation.getRawArguments().length <= 2) {
|
||||
incorrectUsage(invocation.getSource(), "blacklist add <IP address>");
|
||||
if (args.length <= 2) {
|
||||
incorrectUsage(source, "blacklist add <IP address>");
|
||||
return;
|
||||
}
|
||||
|
||||
final String rawAddress = validateIP(invocation.getSource(), invocation.getRawArguments()[2]);
|
||||
final String rawAddress = validateIP(source, args[2]);
|
||||
// Make sure the given IP address is valid
|
||||
if (rawAddress == null) return;
|
||||
|
||||
if (Sonar.get().getFallback().getBlacklist().asMap().containsKey(rawAddress)) {
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.blacklist.ip-duplicate"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix()),
|
||||
Placeholder.unparsed("ip", rawAddress)));
|
||||
@@ -53,7 +53,7 @@ public final class BlacklistCommand extends Subcommand {
|
||||
}
|
||||
|
||||
Sonar.get().getFallback().getBlacklist().put(rawAddress, 1337 /* arbitrarily high number */);
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.blacklist.add"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix()),
|
||||
Placeholder.unparsed("ip", rawAddress)));
|
||||
@@ -61,24 +61,24 @@ public final class BlacklistCommand extends Subcommand {
|
||||
}
|
||||
|
||||
case "remove": {
|
||||
if (invocation.getRawArguments().length <= 2) {
|
||||
incorrectUsage(invocation.getSource(), "blacklist remove <IP address>");
|
||||
if (args.length <= 2) {
|
||||
incorrectUsage(source, "blacklist remove <IP address>");
|
||||
return;
|
||||
}
|
||||
|
||||
final String rawAddress = validateIP(invocation.getSource(), invocation.getRawArguments()[2]);
|
||||
final String rawAddress = validateIP(source, args[2]);
|
||||
// Make sure the given IP address is valid
|
||||
if (rawAddress == null) return;
|
||||
|
||||
if (!Sonar.get().getFallback().getBlacklist().asMap().containsKey(rawAddress)) {
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.blacklist.ip-not-found"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix())));
|
||||
return;
|
||||
}
|
||||
|
||||
Sonar.get().getFallback().getBlacklist().invalidate(rawAddress);
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.blacklist.remove"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix()),
|
||||
Placeholder.unparsed("ip", rawAddress)));
|
||||
@@ -89,14 +89,14 @@ public final class BlacklistCommand extends Subcommand {
|
||||
final long blacklistSize = Sonar.get().getFallback().getBlacklist().estimatedSize();
|
||||
|
||||
if (blacklistSize == 0) {
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.blacklist.empty"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix())));
|
||||
return;
|
||||
}
|
||||
|
||||
Sonar.get().getFallback().getBlacklist().invalidateAll();
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.blacklist.clear"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix()),
|
||||
Placeholder.unparsed("amount", Sonar.DECIMAL_FORMAT.format(blacklistSize))));
|
||||
@@ -106,7 +106,7 @@ public final class BlacklistCommand extends Subcommand {
|
||||
case "size": {
|
||||
final long blacklistSize = Sonar.get().getFallback().getBlacklist().estimatedSize();
|
||||
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.blacklist.size"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix()),
|
||||
Placeholder.unparsed("amount", Sonar.DECIMAL_FORMAT.format(blacklistSize))));
|
||||
@@ -114,7 +114,7 @@ public final class BlacklistCommand extends Subcommand {
|
||||
}
|
||||
|
||||
default: {
|
||||
incorrectUsage(invocation.getSource());
|
||||
incorrectUsage(source);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.SonarPlatform;
|
||||
import xyz.jonesdev.sonar.api.command.CommandInvocation;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.Subcommand;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.SubcommandInfo;
|
||||
|
||||
@@ -46,7 +46,7 @@ public final class DumpCommand extends Subcommand {
|
||||
private static final Gson GSON = new GsonBuilder().create();
|
||||
|
||||
@Override
|
||||
protected void execute(final @NotNull CommandInvocation invocation) {
|
||||
protected void execute(final @NotNull InvocationSource source, final String @NotNull [] args) {
|
||||
final var mappings = new WeakHashMap<String, Object>();
|
||||
mappings.put("sonar", new Dump.Sonar(
|
||||
Sonar.get().getVersion().getFormatted(),
|
||||
|
||||
@@ -21,7 +21,7 @@ import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.command.CommandInvocation;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.Subcommand;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.SubcommandInfo;
|
||||
|
||||
@@ -32,17 +32,17 @@ import xyz.jonesdev.sonar.api.command.subcommand.SubcommandInfo;
|
||||
public final class NotifyCommand extends Subcommand {
|
||||
|
||||
@Override
|
||||
protected void execute(final @NotNull CommandInvocation invocation) {
|
||||
if (Sonar.get().getChatNotificationHandler().isSubscribed(invocation.getSource().getUuid())) {
|
||||
Sonar.get().getChatNotificationHandler().unsubscribe(invocation.getSource().getUuid());
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
protected void execute(final @NotNull InvocationSource source, final String @NotNull [] args) {
|
||||
if (Sonar.get().getChatNotificationHandler().isSubscribed(source.getUuid())) {
|
||||
Sonar.get().getChatNotificationHandler().unsubscribe(source.getUuid());
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.notify.unsubscribe"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix())));
|
||||
return;
|
||||
}
|
||||
|
||||
Sonar.get().getChatNotificationHandler().subscribe(invocation.getSource().getUuid());
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getChatNotificationHandler().subscribe(source.getUuid());
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.notify.subscribe"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix())));
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.command.CommandInvocation;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.Subcommand;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.SubcommandInfo;
|
||||
import xyz.jonesdev.sonar.api.timer.SystemTimer;
|
||||
@@ -32,8 +32,8 @@ import xyz.jonesdev.sonar.api.timer.SystemTimer;
|
||||
public final class ReloadCommand extends Subcommand {
|
||||
|
||||
@Override
|
||||
protected void execute(final @NotNull CommandInvocation invocation) {
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
protected void execute(final @NotNull InvocationSource source, final String @NotNull [] args) {
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.reload.start"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix())));
|
||||
|
||||
@@ -41,7 +41,7 @@ public final class ReloadCommand extends Subcommand {
|
||||
|
||||
Sonar.get().reload();
|
||||
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.reload.finish"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix()),
|
||||
Placeholder.unparsed("time-taken", String.valueOf(timer.delay()))));
|
||||
|
||||
@@ -23,11 +23,12 @@ import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.command.CommandInvocation;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.Subcommand;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.SubcommandInfo;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.Sonar.DECIMAL_FORMAT;
|
||||
import java.util.List;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.profiler.SimpleProcessProfiler.*;
|
||||
|
||||
@SubcommandInfo(
|
||||
@@ -39,13 +40,13 @@ import static xyz.jonesdev.sonar.api.profiler.SimpleProcessProfiler.*;
|
||||
public final class StatisticsCommand extends Subcommand {
|
||||
|
||||
@Override
|
||||
protected void execute(final @NotNull CommandInvocation invocation) {
|
||||
protected void execute(final @NotNull InvocationSource source, final String @NotNull [] args) {
|
||||
String type = "general";
|
||||
if (invocation.getRawArguments().length >= 2) {
|
||||
if (args.length >= 2) {
|
||||
try {
|
||||
type = invocation.getRawArguments()[1].toLowerCase();
|
||||
type = args[1].toLowerCase();
|
||||
} catch (Exception exception) {
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.statistics.unknown-type"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix()),
|
||||
Placeholder.unparsed("statistics", getArguments())));
|
||||
@@ -53,11 +54,11 @@ public final class StatisticsCommand extends Subcommand {
|
||||
}
|
||||
}
|
||||
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.statistics.header"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix()),
|
||||
Placeholder.unparsed("statistics-type", type)));
|
||||
invocation.getSource().sendMessage(Component.empty());
|
||||
source.sendMessage(Component.empty());
|
||||
|
||||
TagResolver.@NotNull Single[] placeholders = null;
|
||||
|
||||
@@ -67,17 +68,18 @@ public final class StatisticsCommand extends Subcommand {
|
||||
final long days = seconds / (24L * 60L * 60L);
|
||||
final long hours = (seconds % (24L * 60L * 60L)) / (60L * 60L);
|
||||
final long minutes = (seconds % (60L * 60L)) / 60L;
|
||||
final String serverUptime = String.format("%dd %dh %dm %ds", days, hours, minutes, seconds % 60L);
|
||||
|
||||
placeholders = new TagResolver.Single[]{
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix()),
|
||||
Placeholder.unparsed("verified", DECIMAL_FORMAT.format(Sonar.get().getVerifiedPlayerController().estimatedSize())),
|
||||
Placeholder.unparsed("verifying", DECIMAL_FORMAT.format(Sonar.get().getFallback().getConnected().size())),
|
||||
Placeholder.unparsed("blacklisted", DECIMAL_FORMAT.format(Sonar.get().getFallback().getBlacklist().estimatedSize())),
|
||||
Placeholder.unparsed("queued", DECIMAL_FORMAT.format(Sonar.get().getFallback().getQueue().getPlayers().size())),
|
||||
Placeholder.unparsed("server-uptime", String.format("%dd %dh %dm %ds", days, hours, minutes, seconds % 60L)),
|
||||
Placeholder.unparsed("total-joins", DECIMAL_FORMAT.format(Sonar.get().getStatistics().getTotalPlayersJoined())),
|
||||
Placeholder.unparsed("total-attempts", DECIMAL_FORMAT.format(Sonar.get().getStatistics().getTotalAttemptedVerifications())),
|
||||
Placeholder.unparsed("total-failed", DECIMAL_FORMAT.format(Sonar.get().getStatistics().getTotalFailedVerifications()))
|
||||
Placeholder.unparsed("verified", Sonar.DECIMAL_FORMAT.format(Sonar.get().getVerifiedPlayerController().getCache().size())),
|
||||
Placeholder.unparsed("verifying", Sonar.DECIMAL_FORMAT.format(Sonar.get().getFallback().getConnected().size())),
|
||||
Placeholder.unparsed("blacklisted", Sonar.DECIMAL_FORMAT.format(Sonar.get().getFallback().getBlacklist().estimatedSize())),
|
||||
Placeholder.unparsed("queued", Sonar.DECIMAL_FORMAT.format(Sonar.get().getFallback().getQueue().getPlayers().size())),
|
||||
Placeholder.unparsed("server-uptime", serverUptime),
|
||||
Placeholder.unparsed("total-joins", Sonar.DECIMAL_FORMAT.format(Sonar.get().getStatistics().getTotalPlayersJoined())),
|
||||
Placeholder.unparsed("total-attempts", Sonar.DECIMAL_FORMAT.format(Sonar.get().getStatistics().getTotalAttemptedVerifications())),
|
||||
Placeholder.unparsed("total-failed", Sonar.DECIMAL_FORMAT.format(Sonar.get().getStatistics().getTotalFailedVerifications()))
|
||||
};
|
||||
break;
|
||||
}
|
||||
@@ -85,11 +87,11 @@ public final class StatisticsCommand extends Subcommand {
|
||||
case "cpu": {
|
||||
placeholders = new TagResolver.Single[]{
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix()),
|
||||
Placeholder.unparsed("process-cpu", DECIMAL_FORMAT.format(getProcessCPUUsage())),
|
||||
Placeholder.unparsed("system-cpu", DECIMAL_FORMAT.format(getSystemCPUUsage())),
|
||||
Placeholder.unparsed("average-process-cpu", DECIMAL_FORMAT.format(getAverageProcessCPUUsage())),
|
||||
Placeholder.unparsed("average-system-cpu", DECIMAL_FORMAT.format(getAverageSystemCPUUsage())),
|
||||
Placeholder.unparsed("virtual-core-count", DECIMAL_FORMAT.format(getVirtualCores()))
|
||||
Placeholder.unparsed("process-cpu", Sonar.DECIMAL_FORMAT.format(getProcessCPUUsage())),
|
||||
Placeholder.unparsed("system-cpu", Sonar.DECIMAL_FORMAT.format(getSystemCPUUsage())),
|
||||
Placeholder.unparsed("average-process-cpu", Sonar.DECIMAL_FORMAT.format(getAverageProcessCPUUsage())),
|
||||
Placeholder.unparsed("average-system-cpu", Sonar.DECIMAL_FORMAT.format(getAverageSystemCPUUsage())),
|
||||
Placeholder.unparsed("virtual-core-count", Sonar.DECIMAL_FORMAT.format(getVirtualCores()))
|
||||
};
|
||||
break;
|
||||
}
|
||||
@@ -117,8 +119,10 @@ public final class StatisticsCommand extends Subcommand {
|
||||
}
|
||||
}
|
||||
|
||||
for (final String msg : Sonar.get().getConfig().getMessagesConfig().getStringList("commands.statistics." + type)) {
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(msg, placeholders));
|
||||
final List<String> parts = Sonar.get().getConfig().getMessagesConfig().getStringList(
|
||||
"commands.statistics." + type);
|
||||
for (final String msg : parts) {
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(msg, placeholders));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.command.CommandInvocation;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.Subcommand;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.SubcommandInfo;
|
||||
|
||||
@@ -33,19 +33,19 @@ import xyz.jonesdev.sonar.api.command.subcommand.SubcommandInfo;
|
||||
public final class VerboseCommand extends Subcommand {
|
||||
|
||||
@Override
|
||||
protected void execute(final @NotNull CommandInvocation invocation) {
|
||||
if (Sonar.get().getActionBarNotificationHandler().isSubscribed(invocation.getSource().getUuid())) {
|
||||
Sonar.get().getActionBarNotificationHandler().unsubscribe(invocation.getSource().getUuid());
|
||||
protected void execute(final @NotNull InvocationSource source, final String @NotNull [] args) {
|
||||
if (Sonar.get().getActionBarNotificationHandler().isSubscribed(source.getUuid())) {
|
||||
Sonar.get().getActionBarNotificationHandler().unsubscribe(source.getUuid());
|
||||
// Reset ActionBar component when unsubscribing
|
||||
invocation.getSource().getAudience().sendActionBar(Component.empty());
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
source.getAudience().sendActionBar(Component.empty());
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.verbose.unsubscribe"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix())));
|
||||
return;
|
||||
}
|
||||
|
||||
Sonar.get().getActionBarNotificationHandler().subscribe(invocation.getSource().getUuid());
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getActionBarNotificationHandler().subscribe(source.getUuid());
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.verbose.subscribe"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix())));
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.command.CommandInvocation;
|
||||
import xyz.jonesdev.sonar.api.command.InvocationSource;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.Subcommand;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.SubcommandInfo;
|
||||
import xyz.jonesdev.sonar.api.database.model.VerifiedPlayer;
|
||||
@@ -34,30 +34,30 @@ import xyz.jonesdev.sonar.api.fingerprint.FingerprintingUtil;
|
||||
public final class VerifiedCommand extends Subcommand {
|
||||
|
||||
@Override
|
||||
protected void execute(final @NotNull CommandInvocation invocation) {
|
||||
switch (invocation.getRawArguments()[1].toLowerCase()) {
|
||||
protected void execute(final @NotNull InvocationSource source, final String @NotNull [] args) {
|
||||
switch (args[1].toLowerCase()) {
|
||||
case "remove": {
|
||||
if (invocation.getRawArguments().length <= 3) {
|
||||
incorrectUsage(invocation.getSource(), "verified remove <IP address> <username>");
|
||||
if (args.length <= 3) {
|
||||
incorrectUsage(source, "verified remove <IP address> <username>");
|
||||
return;
|
||||
}
|
||||
|
||||
final String hostAddress = validateIP(invocation.getSource(), invocation.getRawArguments()[2]);
|
||||
final String hostAddress = validateIP(source, args[2]);
|
||||
// Make sure the given IP address is valid
|
||||
if (hostAddress == null) return;
|
||||
|
||||
final String username = invocation.getRawArguments()[3];
|
||||
final String username = args[3];
|
||||
final String fingerprint = FingerprintingUtil.getFingerprint(username, hostAddress);
|
||||
|
||||
if (!Sonar.get().getVerifiedPlayerController().getCache().contains(fingerprint)) {
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.verified.ip-not-found"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix())));
|
||||
return;
|
||||
}
|
||||
|
||||
Sonar.get().getVerifiedPlayerController().remove(fingerprint);
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.verified.remove"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix()),
|
||||
Placeholder.unparsed("ip", hostAddress),
|
||||
@@ -66,27 +66,27 @@ public final class VerifiedCommand extends Subcommand {
|
||||
}
|
||||
|
||||
case "add": {
|
||||
if (invocation.getRawArguments().length <= 3) {
|
||||
incorrectUsage(invocation.getSource(), "verified add <IP address> <username>");
|
||||
if (args.length <= 3) {
|
||||
incorrectUsage(source, "verified add <IP address> <username>");
|
||||
return;
|
||||
}
|
||||
|
||||
final String hostAddress = validateIP(invocation.getSource(), invocation.getRawArguments()[2]);
|
||||
final String hostAddress = validateIP(source, args[2]);
|
||||
// Make sure the given IP address is valid
|
||||
if (hostAddress == null) return;
|
||||
|
||||
final String username = invocation.getRawArguments()[3];
|
||||
final String username = args[3];
|
||||
final String fingerprint = FingerprintingUtil.getFingerprint(username, hostAddress);
|
||||
|
||||
if (Sonar.get().getVerifiedPlayerController().getCache().contains(fingerprint)) {
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.verified.already"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix())));
|
||||
return;
|
||||
}
|
||||
|
||||
Sonar.get().getVerifiedPlayerController().add(new VerifiedPlayer(fingerprint, System.currentTimeMillis()));
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.verified.add"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix()),
|
||||
Placeholder.unparsed("ip", hostAddress),
|
||||
@@ -95,17 +95,17 @@ public final class VerifiedCommand extends Subcommand {
|
||||
}
|
||||
|
||||
case "clear": {
|
||||
final int verifiedSize = Sonar.get().getVerifiedPlayerController().estimatedSize();
|
||||
final int verifiedSize = Sonar.get().getVerifiedPlayerController().getCache().size();
|
||||
|
||||
if (verifiedSize == 0) {
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.verified.empty"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix())));
|
||||
return;
|
||||
}
|
||||
|
||||
Sonar.get().getVerifiedPlayerController().clearAll();
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.verified.clear"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix()),
|
||||
Placeholder.unparsed("amount", Sonar.DECIMAL_FORMAT.format(verifiedSize))));
|
||||
@@ -113,9 +113,9 @@ public final class VerifiedCommand extends Subcommand {
|
||||
}
|
||||
|
||||
case "size": {
|
||||
final int verifiedSize = Sonar.get().getVerifiedPlayerController().estimatedSize();
|
||||
final int verifiedSize = Sonar.get().getVerifiedPlayerController().getCache().size();
|
||||
|
||||
invocation.getSource().sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
source.sendMessage(MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get().getConfig().getMessagesConfig().getString("commands.verified.size"),
|
||||
Placeholder.component("prefix", Sonar.get().getConfig().getPrefix()),
|
||||
Placeholder.unparsed("amount", Sonar.DECIMAL_FORMAT.format(verifiedSize))));
|
||||
@@ -123,7 +123,7 @@ public final class VerifiedCommand extends Subcommand {
|
||||
}
|
||||
|
||||
default: {
|
||||
incorrectUsage(invocation.getSource());
|
||||
incorrectUsage(source);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,16 +31,15 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_20_3;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeNamelessCompoundTag;
|
||||
import static xyz.jonesdev.sonar.common.util.ProtocolUtil.writeString;
|
||||
|
||||
public final class ComponentHolder {
|
||||
private final String serializedComponent;
|
||||
private BinaryTag cachedBinaryTag;
|
||||
private final String modernJson;
|
||||
private final String legacyJson;
|
||||
private final BinaryTag binaryTag;
|
||||
|
||||
public ComponentHolder(final @NotNull Component component) {
|
||||
this.serializedComponent = GsonComponentSerializer.gson().serialize(component);
|
||||
this.modernJson = GsonComponentSerializer.gson().serialize(component);
|
||||
this.legacyJson = GsonComponentSerializer.colorDownsamplingGson().serialize(component);
|
||||
this.binaryTag = serialize(new JsonParser().parse(this.modernJson));
|
||||
}
|
||||
|
||||
// https://github.com/PaperMC/Velocity/blob/dev/3.0.0/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ComponentHolder.java
|
||||
@@ -141,14 +140,12 @@ public final class ComponentHolder {
|
||||
return EndBinaryTag.endBinaryTag();
|
||||
}
|
||||
|
||||
public void write(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion) {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_20_3) >= 0) {
|
||||
if (cachedBinaryTag == null) {
|
||||
cachedBinaryTag = serialize(new JsonParser().parse(serializedComponent));
|
||||
}
|
||||
writeNamelessCompoundTag(byteBuf, cachedBinaryTag);
|
||||
public void write(final ByteBuf byteBuf, final @NotNull ProtocolVersion protocolVersion, final boolean forceJson) {
|
||||
if (!forceJson && protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_20_3)) {
|
||||
ProtocolUtil.writeBinaryTag(byteBuf, protocolVersion, binaryTag);
|
||||
} else {
|
||||
writeString(byteBuf, serializedComponent);
|
||||
ProtocolUtil.writeString(byteBuf,
|
||||
protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_16) ? modernJson : legacyJson);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,26 +47,23 @@ import io.netty.handler.codec.DecoderException;
|
||||
import io.netty.handler.codec.EncoderException;
|
||||
import io.netty.util.Version;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import net.kyori.adventure.nbt.*;
|
||||
import net.kyori.adventure.nbt.BinaryTag;
|
||||
import net.kyori.adventure.nbt.BinaryTagType;
|
||||
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.dimension.DimensionRegistry;
|
||||
import xyz.jonesdev.sonar.common.util.exception.QuietDecoderException;
|
||||
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_7_2;
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.MINECRAFT_1_8;
|
||||
|
||||
// https://github.com/PaperMC/Velocity/blob/dev/3.0.0/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java
|
||||
@UtilityClass
|
||||
public class ProtocolUtil {
|
||||
public static final boolean DEBUG = Boolean.getBoolean("sonar.debug-traces");
|
||||
public static final String BRAND_CHANNEL_LEGACY = "MC|Brand";
|
||||
public static final String BRAND_CHANNEL = "minecraft:brand";
|
||||
private static final int[] VAR_INT_LENGTHS = new int[65];
|
||||
|
||||
static {
|
||||
@@ -111,7 +108,7 @@ public class ProtocolUtil {
|
||||
if ((value & (0xFFFFFFFF << 7)) == 0) {
|
||||
byteBuf.writeByte(value);
|
||||
} else if ((value & (0xFFFFFFFF << 14)) == 0) {
|
||||
final int w = (value & 0x7F | 0x80) << 8 | (value >>> 7);
|
||||
int w = (value & 0x7F | 0x80) << 8 | (value >>> 7);
|
||||
byteBuf.writeShort(w);
|
||||
} else {
|
||||
writeVarIntFull(byteBuf, value);
|
||||
@@ -224,9 +221,8 @@ public class ProtocolUtil {
|
||||
|
||||
public static void closeWith(final @NotNull Channel channel,
|
||||
final @NotNull ProtocolVersion protocolVersion,
|
||||
final Object msg) {
|
||||
if (protocolVersion.compareTo(MINECRAFT_1_8) < 0
|
||||
&& protocolVersion.compareTo(MINECRAFT_1_7_2) >= 0) {
|
||||
final @NotNull Object msg) {
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
channel.eventLoop().execute(() -> {
|
||||
channel.config().setAutoRead(false);
|
||||
channel.eventLoop().schedule(() -> {
|
||||
@@ -284,15 +280,7 @@ public class ProtocolUtil {
|
||||
byteBuf.writeLong(uuid.getLeastSignificantBits());
|
||||
}
|
||||
|
||||
public static void writeUUIDIntArray(final @NotNull ByteBuf byteBuf, final @NotNull UUID uuid) {
|
||||
byteBuf.writeInt((int) (uuid.getMostSignificantBits() >> 32));
|
||||
byteBuf.writeInt((int) uuid.getMostSignificantBits());
|
||||
byteBuf.writeInt((int) (uuid.getLeastSignificantBits() >> 32));
|
||||
byteBuf.writeInt((int) uuid.getLeastSignificantBits());
|
||||
}
|
||||
|
||||
public static void writeByteArray(final @NotNull ByteBuf byteBuf, final byte @NotNull [] bytes) {
|
||||
checkState(bytes.length < Short.MAX_VALUE, "Too long array");
|
||||
writeVarInt(byteBuf, bytes.length);
|
||||
byteBuf.writeBytes(bytes);
|
||||
}
|
||||
@@ -304,49 +292,21 @@ public class ProtocolUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeCompoundTag(final @NotNull ByteBuf byteBuf, final @NotNull CompoundBinaryTag compoundTag) {
|
||||
// https://github.com/PaperMC/Velocity/blob/dev/3.0.0/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java#L458
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends BinaryTag> void writeBinaryTag(final @NotNull ByteBuf byteBuf,
|
||||
final @NotNull ProtocolVersion protocolVersion,
|
||||
final @NotNull T tag) {
|
||||
final BinaryTagType<T> type = (BinaryTagType<T>) tag.type();
|
||||
byteBuf.writeByte(type.id());
|
||||
try {
|
||||
BinaryTagIO.writer().write(compoundTag, (DataOutput) new ByteBufOutputStream(byteBuf));
|
||||
} catch (IOException exception) {
|
||||
throw new EncoderException("Unable to encode NBT CompoundTag");
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/Nan1t/NanoLimbo/blob/main/src/main/java/ua/nanit/limbo/protocol/ByteMessage.java#L219
|
||||
public static void writeNamelessCompoundTag(final @NotNull ByteBuf byteBuf, final @NotNull BinaryTag binaryTag) {
|
||||
try (final ByteBufOutputStream output = new ByteBufOutputStream(byteBuf)) {
|
||||
// TODO: Find a way to improve this...
|
||||
output.writeByte(binaryTag.type().id());
|
||||
if (binaryTag instanceof CompoundBinaryTag) {
|
||||
CompoundBinaryTag tag = (CompoundBinaryTag) binaryTag;
|
||||
tag.type().write(tag, output);
|
||||
} else if (binaryTag instanceof ByteBinaryTag) {
|
||||
ByteBinaryTag tag = (ByteBinaryTag) binaryTag;
|
||||
tag.type().write(tag, output);
|
||||
} else if (binaryTag instanceof ShortBinaryTag) {
|
||||
ShortBinaryTag tag = (ShortBinaryTag) binaryTag;
|
||||
tag.type().write(tag, output);
|
||||
} else if (binaryTag instanceof IntBinaryTag) {
|
||||
IntBinaryTag tag = (IntBinaryTag) binaryTag;
|
||||
tag.type().write(tag, output);
|
||||
} else if (binaryTag instanceof LongBinaryTag) {
|
||||
LongBinaryTag tag = (LongBinaryTag) binaryTag;
|
||||
tag.type().write(tag, output);
|
||||
} else if (binaryTag instanceof DoubleBinaryTag) {
|
||||
DoubleBinaryTag tag = (DoubleBinaryTag) binaryTag;
|
||||
tag.type().write(tag, output);
|
||||
} else if (binaryTag instanceof StringBinaryTag) {
|
||||
StringBinaryTag tag = (StringBinaryTag) binaryTag;
|
||||
tag.type().write(tag, output);
|
||||
} else if (binaryTag instanceof ListBinaryTag) {
|
||||
ListBinaryTag tag = (ListBinaryTag) binaryTag;
|
||||
tag.type().write(tag, output);
|
||||
} else if (binaryTag instanceof EndBinaryTag) {
|
||||
EndBinaryTag tag = (EndBinaryTag) binaryTag;
|
||||
tag.type().write(tag, output);
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_2)) {
|
||||
// pre-1.20.2 clients need an empty name
|
||||
byteBuf.writeShort(0);
|
||||
}
|
||||
type.write(tag, new ByteBufOutputStream(byteBuf));
|
||||
} catch (IOException exception) {
|
||||
throw new EncoderException("Unable to encode NBT CompoundTag");
|
||||
throw new EncoderException("Unable to encode BinaryTag", exception);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,6 +320,25 @@ public class ProtocolUtil {
|
||||
return ((high & 0xFF) << 15) | low;
|
||||
}
|
||||
|
||||
public static CompoundBinaryTag getCodec(final @NotNull ProtocolVersion protocolVersion) {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21)) {
|
||||
return DimensionRegistry.CODEC_1_21;
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_20)) {
|
||||
return DimensionRegistry.CODEC_1_20;
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_19_4)) {
|
||||
return DimensionRegistry.CODEC_1_19_4;
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_19_1)) {
|
||||
return DimensionRegistry.CODEC_1_19_1;
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_19)) {
|
||||
return DimensionRegistry.CODEC_1_19;
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_18_2)) {
|
||||
return DimensionRegistry.CODEC_1_18_2;
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_16_2)) {
|
||||
return DimensionRegistry.CODEC_1_16_2;
|
||||
}
|
||||
return DimensionRegistry.CODEC_1_16;
|
||||
}
|
||||
|
||||
private void checkState(final boolean expression, final String message) {
|
||||
if (!expression) {
|
||||
throw DEBUG ? new DecoderException(message) : QuietDecoderException.INSTANCE;
|
||||
|
||||
Binary file not shown.
@@ -165,6 +165,12 @@ verification:
|
||||
# - CREATIVE: životy a hunger neni zobrazen
|
||||
# - ADVENTURE: všechny komponenty UI jsou zobrazeny
|
||||
gamemode: ADVENTURE
|
||||
# Letní čas v ověřovacím světě
|
||||
# Všechny letní časy najdete zde:
|
||||
# https://minecraft.wiki/w/Daylight_cycle
|
||||
# Jedná se pouze o vizuální nastavení, které nemění proces ověřování!
|
||||
# Pokud je tato hodnota nastavena na 1000, paket nebude odeslán.
|
||||
time-of-day: 1000
|
||||
# Sonar ukládá do mezipaměti většinu paketů, které odesílá klientovi, včetně paketu LoginSuccess.
|
||||
# Které uživatelské jméno má Sonar použít pro paket LoginSuccess?
|
||||
# Uživatelské jméno nebude pro hráče viditelné bez úpravy klienta
|
||||
@@ -188,7 +194,7 @@ verification:
|
||||
write-timeout: 10000
|
||||
# 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
|
||||
rejoin-delay: 5000
|
||||
# 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
|
||||
|
||||
@@ -162,6 +162,12 @@ verification:
|
||||
# - CREATIVE: Gesundheit und Hunger sind versteckt
|
||||
# - ADVENTURE: alle UI-Komponenten sind sichtbar
|
||||
gamemode: ADVENTURE
|
||||
# Tageszeit in der Verifizierungswelt
|
||||
# Alle Tageszeiten finden Sie hier:
|
||||
# https://minecraft.wiki/w/Daylight_cycle
|
||||
# Dies ist nur eine visuelle Einstellung und ändert nichts am Verifizierungsprozess!
|
||||
# Wenn dieser Wert auf 1000 gesetzt ist, wird das Paket nicht gesendet.
|
||||
time-of-day: 1000
|
||||
# Sonar speichert die meisten Pakete, die es an den Client sendet, einschließlich des LoginSuccess-Pakets.
|
||||
# Welchen Benutzernamen soll Sonar für das LoginSuccess-Paket verwenden?
|
||||
# Der Benutzername ist für den Spieler ohne Änderungen am Client nicht sichtbar
|
||||
@@ -185,7 +191,7 @@ verification:
|
||||
write-timeout: 10000
|
||||
# Wie lange sollte ein Spieler warten, bevor er während der Überprüfung erneut verbinden kann
|
||||
# (Dieser Wert stellt die Zeit in Millisekunden dar: 1 Sekunde = 1000 Millisekunden)
|
||||
rejoin-delay: 8000
|
||||
rejoin-delay: 5000
|
||||
# How long should Sonar remember the amount of failed verifications for a player?
|
||||
# (Dieser Wert stellt die Zeit in Millisekunden dar: 1 Sekunde = 1000 Millisekunden)
|
||||
remember-time: 120000
|
||||
|
||||
@@ -165,6 +165,12 @@ verification:
|
||||
# - CREATIVE: health and hunger are hidden
|
||||
# - ADVENTURE: all UI components are visible
|
||||
gamemode: ADVENTURE
|
||||
# Daylight time in the verification world
|
||||
# You can find all daylight times here:
|
||||
# https://minecraft.wiki/w/Daylight_cycle
|
||||
# This is only a visual setting and does not change the verification process!
|
||||
# If this value is set to 1000, the packet will not be sent.
|
||||
time-of-day: 1000
|
||||
# Sonar caches most packets it sends to the client, including the LoginSuccess packet.
|
||||
# Which username should Sonar use for the LoginSuccess packet?
|
||||
# The username will not be visible to the player without client modifications
|
||||
@@ -188,7 +194,7 @@ verification:
|
||||
write-timeout: 10000
|
||||
# How long should a player wait before reconnecting during verification
|
||||
# (This value represents the time in milliseconds: 1 second = 1000 milliseconds)
|
||||
rejoin-delay: 8000
|
||||
rejoin-delay: 5000
|
||||
# How long should Sonar remember the number of failed verifications for a player?
|
||||
# (This value represents the time in milliseconds: 1 second = 1000 milliseconds)
|
||||
remember-time: 120000
|
||||
|
||||
@@ -164,6 +164,12 @@ verification:
|
||||
# - CREATIVE : la santé et la faim sont cachées
|
||||
# - ADVENTURE : tous les composants de l'interface utilisateur sont visibles
|
||||
gamemode: ADVENTURE
|
||||
# L'heure d'été dans le monde de la vérification
|
||||
# Vous pouvez trouver toutes les heures d'été ici :
|
||||
# https://minecraft.wiki/w/Daylight_cycle
|
||||
# Il s'agit uniquement d'un paramètre visuel qui ne modifie pas le processus de vérification !
|
||||
# Si cette valeur est fixée à 1000, le paquet ne sera pas envoyé.
|
||||
time-of-day: 1000
|
||||
# Sonar met en cache la plupart des paquets qu'il envoie au client, y compris le paquet LoginSuccess.
|
||||
# Quel nom d'utilisateur Sonar doit-il utiliser pour le paquet LoginSuccess ?
|
||||
# Le nom d'utilisateur ne sera pas visible par le joueur sans modification du client
|
||||
@@ -187,7 +193,7 @@ verification:
|
||||
write-timeout: 10000
|
||||
# Combien de temps un joueur doit-il attendre avant de se reconnecter pendant la vérification
|
||||
# (Cette valeur représente le temps en millisecondes : 1 seconde = 1000 millisecondes)
|
||||
rejoin-delay: 8000
|
||||
rejoin-delay: 5000
|
||||
# Combien de temps Sonar doit-il se souvenir du nombre de vérifications échouées pour un joueur ?
|
||||
# (Cette valeur représente le temps en millisecondes : 1 seconde = 1000 millisecondes)
|
||||
remember-time: 120000
|
||||
|
||||
@@ -163,6 +163,12 @@ verification:
|
||||
# - CREATIVE: სიცოცხლის და საჭმლის მაჩვენებლები იშლება
|
||||
# - ADVENTURE: ყველა UI კომპონენტი ჩანს
|
||||
gamemode: ADVENTURE
|
||||
# Daylight time in the verification world
|
||||
# You can find all daylight times here:
|
||||
# https://minecraft.wiki/w/Daylight_cycle
|
||||
# This is only a visual setting and does not change the verification process!
|
||||
# If this value is set to 1000, the packet will not be sent.
|
||||
time-of-day: 1000
|
||||
# Sonar caches most packets it sends to the client, including the LoginSuccess packet.
|
||||
# Which username should Sonar use for the LoginSuccess packet?
|
||||
# The username will not be visible to the player without client modifications
|
||||
@@ -186,7 +192,7 @@ verification:
|
||||
write-timeout: 10000
|
||||
# რამდენ ხანს უნდა დაელოდოს მოთამაშე ვერიფიკაციის დროს ხელახლა დაკავშირებამდე?
|
||||
# (ეს არის ნაჩვენები მილიწამებში: 1 წამი = 1000 მილიწამი)
|
||||
rejoin-delay: 8000
|
||||
rejoin-delay: 5000
|
||||
# რამდენ ხანს უნდა ახსოვდეს Sonar-ს მოთამაშე რამდენჯერ იყო წარუმატებელი ვერიფიკაციის დროს?
|
||||
# (ეს არის ნაჩვენები მილიწამებში: 1 წამი = 1000 მილიწამი)
|
||||
remember-time: 120000
|
||||
|
||||
@@ -164,6 +164,12 @@ verification:
|
||||
# - CREATIVE: gezondheid en honger zijn verborgen
|
||||
# - ADVENTURE: alle UI-componenten zijn zichtbaar
|
||||
gamemode: ADVENTURE
|
||||
# Zomertijd in de verificatiewereld
|
||||
# Je kunt alle zomertijd hier vinden:
|
||||
# https://minecraft.wiki/w/Daylight_cycle
|
||||
# Dit is alleen een visuele instelling en verandert niets aan het verificatieproces!
|
||||
# Als deze waarde is ingesteld op 1000, wordt het pakket niet verzonden.
|
||||
time-of-day: 1000
|
||||
# Sonar cached de meeste pakketten die het naar de client stuurt, inclusief het LoginSuccess pakket.
|
||||
# Welke gebruikersnaam moet Sonar gebruiken voor het LoginSucces pakket?
|
||||
# De gebruikersnaam zal niet zichtbaar zijn voor de speler zonder client aanpassingen
|
||||
@@ -184,7 +190,7 @@ verification:
|
||||
read-timeout: 8000
|
||||
# De tijd sinds de speler stopte met het verzenden van pakketten voordat de verbinding werd verbroken
|
||||
# (Deze waarde vertegenwoordigt de tijd in milliseconden: 1 seconde = 1000 milliseconden)
|
||||
rejoin-delay: 8000
|
||||
rejoin-delay: 5000
|
||||
# De tijd sinds de server stopte met het versturen van pakketten voordat de verbinding met de speler werd verbroken
|
||||
# (Deze waarde vertegenwoordigt de tijd in milliseconden: 1 seconde = 1000 milliseconden)
|
||||
write-timeout: 10000
|
||||
|
||||
@@ -164,6 +164,12 @@ verification:
|
||||
# - CREATIVE: pasek życia i głodu jest ukryty
|
||||
# - ADVENTURE: wszystkie komponenty interfejsu są widoczne
|
||||
gamemode: ADVENTURE
|
||||
# Czas letni w weryfikowanym świecie
|
||||
# Wszystkie czasy letnie można znaleźć tutaj:
|
||||
# https://minecraft.wiki/w/Daylight_cycle
|
||||
# Jest to tylko ustawienie wizualne i nie zmienia procesu weryfikacji!
|
||||
# Jeśli ta wartość jest ustawiona na 1000, pakiet nie zostanie wysłany.
|
||||
time-of-day: 1000
|
||||
# Sonar buforuje większość pakietów, które wysyła do klienta, w tym pakiet LoginSuccess.
|
||||
# Jakiej nazwy użytkownika powinien używać Sonar dla pakietu LoginSuccess?
|
||||
# Nazwa użytkownika nie będzie widoczna dla gracza bez modyfikacji klienta
|
||||
@@ -187,7 +193,7 @@ verification:
|
||||
write-timeout: 10000
|
||||
# Jak długo niezweryfikowany gracz powinien czekać przed ponownym połączeniem do serwera?
|
||||
# (Wartość ta reprezentuje czas w milisekundach: 1 sekunda = 1000 milisekund)
|
||||
rejoin-delay: 8000
|
||||
rejoin-delay: 5000
|
||||
# Jak długo Sonar powinien zapamiętywać liczbę nieudanych weryfikacji gracza?
|
||||
# (Wartość ta reprezentuje czas w milisekundach: 1 sekunda = 1000 milisekund)
|
||||
remember-time: 120000
|
||||
|
||||
@@ -163,6 +163,12 @@ verification:
|
||||
# - CREATIVE: здоровье и голод скрыты
|
||||
# - ADVENTURE: все компоненты пользовательского интерфейса видны
|
||||
gamemode: ADVENTURE
|
||||
# Летнее время в мире верификации
|
||||
# Вы можете найти все летнее время здесь:
|
||||
# https://minecraft.wiki/w/Daylight_cycle
|
||||
# Это только визуальная настройка и не меняет процесс верификации!
|
||||
# Если это значение установлено в 1000, пакет не будет отправлен.
|
||||
time-of-day: 1000
|
||||
# Sonar кэширует большинство пакетов, которые он отправляет клиенту, включая пакет LoginSuccess.
|
||||
# Какое имя пользователя должен использовать Sonar для пакета LoginSuccess?
|
||||
# Имя пользователя не будет видно игроку без модификации клиента
|
||||
@@ -186,7 +192,7 @@ verification:
|
||||
write-timeout: 10000
|
||||
# Сколько времени игрок должен ждать перед повторным подключением во время проверки
|
||||
# (Это значение представляет время в миллисекундах: 1 секунда = 1000 миллисекунд)
|
||||
rejoin-delay: 8000
|
||||
rejoin-delay: 5000
|
||||
# Как долго Sonar должен помнить количество неудачных проверок для игрока?
|
||||
# (Это значение представляет время в миллисекундах: 1 секунда = 1000 миллисекунд)
|
||||
remember-time: 120000
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
# 需要帮助吗? https://jonesdev.xyz/discord
|
||||
# 源代码: https://github.com/jonesdevelopment/sonar
|
||||
# 文件: https://docs.jonesdev.xyz/administration/configuring-sonar
|
||||
# Copyright Jones Development and Sonar Contributors, 以 GPL-3.0 许可
|
||||
# Copyright Jones Development and Sonar Contributors, 以 GPL-3.0 许可证分发
|
||||
#
|
||||
|
||||
# 未归类的设置
|
||||
# 常规的设置
|
||||
general:
|
||||
# Sonar 是否应该自动检查新的更新?
|
||||
# Sonar 使用 GitHub API 检查当前版本是否过时
|
||||
@@ -24,18 +24,19 @@ general:
|
||||
# 具有相同 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
|
||||
|
||||
@@ -43,7 +44,7 @@ attack-tracker:
|
||||
# 提示: IP 地址将以纯文本形式保存 请确保您的数据库安全!
|
||||
database:
|
||||
# 要连接到的数据库类型
|
||||
# 可用类型: NONE (不使用数据库), MYSQL, MARIADB, H2
|
||||
# 可用类型: NONE (不使用数据库), MYSQL, MARIADB, H2 (本地数据库)
|
||||
type: NONE
|
||||
# Sonar 插件目录中用于 H2 数据库的文件
|
||||
filename: verified_players
|
||||
@@ -72,31 +73,31 @@ verification:
|
||||
# 可用类型: ALWAYS, DURING_ATTACK, NEVER
|
||||
# - ALWAYS: 玩家始终需要验证 直到他们通过验证为止
|
||||
# - DURING_ATTACK: 玩家仅在服务器疑似遭到攻击时验证
|
||||
# - NEVER: 玩家不会被验证
|
||||
# - NEVER: 永远不检查未被验证过的玩家
|
||||
timing: ALWAYS
|
||||
checks:
|
||||
# 检查玩家的下落移动是否遵循原版客户端.
|
||||
gravity:
|
||||
# Sonar 是否应该检查玩家的下落移动? (推荐启用)
|
||||
enabled: true
|
||||
# 当玩家验证失败时 是否向他们发送验证码?
|
||||
# 当玩家验证失败时 是否向他们发送地图验证码?
|
||||
captcha-on-fail: false
|
||||
# 玩家跌落在平台上多少tick后才能被算作站在平台上?
|
||||
max-movement-ticks: 8
|
||||
|
||||
# 检查玩家是否与下方生成的方块发生碰撞
|
||||
# 检查玩家是否可以正确地站在方块上
|
||||
collision:
|
||||
# Sonar 是否应该生成方块平台以检查玩家下落与方块之间的碰撞? (推荐启用)
|
||||
enabled: true
|
||||
|
||||
# 检查客户端坐在实体上时是否发送了正确的数据包
|
||||
vehicle:
|
||||
# 声纳是否应该检查玩家是否正确进入船只?
|
||||
# Sonar 是否应该让玩家坐在实体上, 以便检查他们的数据包是否合法?
|
||||
enabled: true
|
||||
# 客户端发送的最小数据包数量
|
||||
# 客户端需要发送的所需的最小数据包数量
|
||||
minimum-packets: 2
|
||||
|
||||
# 地图验证码
|
||||
# 手持地图验证码
|
||||
map-captcha:
|
||||
# Sonar 应该在什么时候向玩家发送验证码?
|
||||
# 可用类型: ALWAYS, DURING_ATTACK, NEVER
|
||||
@@ -141,8 +142,8 @@ verification:
|
||||
# 为此您必须在服务器软件的配置文件中允许接受来自transfer的连接.
|
||||
# 此外 您可能希望减少 Velocity 或其它服务器软件/插件的登录速率限制
|
||||
# 因为这可能会阻止玩家快速地重新连接到服务器
|
||||
# 如果你的服务器允许 Bedrock 玩家通过间歇泉加入, 请确保为间歇泉设置了
|
||||
# 传输工具 TransferTool, 以便将 Bedrock 玩家重定向到正确的服务器.
|
||||
# 如果你的服务器允许 Bedrock 玩家通过 Geyser 加入, 请确保为 Geyser 设置了
|
||||
# TransferTool 扩展, 以便将 Bedrock 玩家重定向到正确的服务器.
|
||||
# https://github.com/onebeastchris/TransferTool
|
||||
enabled: false
|
||||
# 当玩家通过验证后, Sonar 应该将玩家转移到哪个服务器
|
||||
@@ -153,22 +154,28 @@ verification:
|
||||
# 如果服务器地址不包含端口 请保留25565.
|
||||
destination-port: 25565
|
||||
|
||||
# 玩家在虚拟服务器中的游戏模式
|
||||
# 玩家正在验证时所使用的游戏模式
|
||||
# 可用类型: SURVIVAL, CREATIVE, ADVENTURE
|
||||
# - SURVIVAL: 生存模式, 所有UI可见
|
||||
# - CREATIVE: 创造模式, 隐藏血量和饱食度
|
||||
# - ADVENTURE: 冒险模式, 所有UI可见
|
||||
gamemode: ADVENTURE
|
||||
# 验证过程中玩家所看到的当前世界中的一天时间
|
||||
# 您可以在这里找到您想要的时间:
|
||||
# https://minecraft.wiki/w/Daylight_cycle
|
||||
# 该选项不会改变验证过程.
|
||||
# 如果该值设置为 1000, 则不会发送数据包.
|
||||
time-of-day: 1000
|
||||
# Sonar 缓存它发送到客户端的大部分数据包, 包括 LoginSuccess 数据包.
|
||||
# Sonar 应该使用哪个用户名来接收 LoginSuccess 数据包?
|
||||
# 如果不修改客户端, 用户名将不会对玩家可见
|
||||
cached-username: "Sonar"
|
||||
# 当 Sonar 尝试验证玩家时 是否在控制台中打印它?
|
||||
# 当 Sonar 尝试验证玩家时 是否在控制台中打印有关玩家的信息?
|
||||
log-connections: true
|
||||
# Sonar 是否应该记录在攻击期间内的验证会话?
|
||||
# Sonar 是否应该在攻击期间内打印日志?
|
||||
log-during-attack: false
|
||||
# Sonar 是否应该记录验证过程中玩家的移动位置的变化?
|
||||
# 有助于发现错误 但不建议在非调试环境下的服务器启用该选项.
|
||||
# 有助于发现错误 但不建议在生产环境下的服务器启用该选项.
|
||||
debug-xyz-positions: false
|
||||
# Sonar 是否应该检查来自 Geyser 的(基岩版)玩家?
|
||||
# 此功能是实验性的. 请报告任何因为误判而导致无法通过检查的问题
|
||||
@@ -180,9 +187,9 @@ verification:
|
||||
# 服务器在断开玩家连接前停止发送数据包的时间
|
||||
# (该值为毫秒. 1秒等于1000毫秒.)
|
||||
write-timeout: 10000
|
||||
# 当玩家在需要验证时(即还未通过检查的情况下)需要等待多少毫秒才可以重新连接?
|
||||
# 当玩家在还未通过检查的情况下断开连接后需要等待多少毫秒才可以重新连接?
|
||||
# (该值为毫秒. 1秒等于1000毫秒.)
|
||||
rejoin-delay: 8000
|
||||
rejoin-delay: 5000
|
||||
# 当玩家验证失败后, Sonar 应该记住他多久?
|
||||
# (该值为毫秒. 1秒等于1000毫秒.)
|
||||
remember-time: 120000
|
||||
|
||||
@@ -199,6 +199,7 @@ verification:
|
||||
incorrect: '<prefix><red>Zadali jste špatnou odpověď! Zkuste to prosím znovu.'
|
||||
|
||||
# Zpráva zobrazená hráči při ověřování
|
||||
# Nastavením této hodnoty na '' uvítací zprávu zcela vypnete
|
||||
welcome: '<prefix><green>Jste ověřováni. Prosím, nehýbejte se; jedná se o automatický proces.'
|
||||
|
||||
# Zpráva o odpojení, která se zobrazí, když se někdo připojí, zatímco captcha ještě není připravena
|
||||
@@ -207,6 +208,11 @@ verification:
|
||||
- <yellow>Vaše anti-bot data ještě nejsou připravena.
|
||||
- <gray>Počkejte prosím několik sekund, než se pokusíte znovu ověřit.
|
||||
- <footer>
|
||||
# Zpráva o odpojení, která se zobrazí, když se někdo připojí s nepodporovanou verzí Minecraftu
|
||||
unsupported-version:
|
||||
- <header>
|
||||
- <red>Tento server nepodporuje vaši verzi Minecraftu.
|
||||
- <footer>
|
||||
# Zpráva o odpojení, která se zobrazí, když se někdo znovu připojí příliš rychle během ověření
|
||||
too-fast-reconnect:
|
||||
- <header>
|
||||
|
||||
Loaded 100 of 115 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user