mirror of
https://github.com/jonesdevelopment/sonar.git
synced 2026-11-05 17:22:34 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* 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.api.command;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
@ToString(of = "rawArguments")
|
||||
public final class CommandInvocation {
|
||||
private final InvocationSource source;
|
||||
private final String[] rawArguments;
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -408,6 +415,7 @@ public final class SonarConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
private int timeOfDay;
|
||||
private boolean checkGeyser;
|
||||
private boolean logConnections;
|
||||
private boolean logDuringAttack;
|
||||
|
||||
+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
|
||||
|
||||
@@ -84,7 +84,7 @@ public final class FallbackInboundHandler extends ChannelInboundHandlerAdapter {
|
||||
*/
|
||||
@Override
|
||||
public void exceptionCaught(final @NotNull ChannelHandlerContext ctx,
|
||||
final @NotNull Throwable cause) throws Exception {
|
||||
final Throwable cause) throws Exception {
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
+5
-4
@@ -25,10 +25,12 @@ 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.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 +40,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
|
||||
@@ -214,7 +215,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 +230,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) {
|
||||
// Remove the connection handler pipeline to completely take over the channel
|
||||
final String handler = Sonar.get().getPlatform().getConnectionHandler();
|
||||
if (channel.pipeline().context(handler) != null) {
|
||||
@@ -244,7 +245,7 @@ public abstract class FallbackInboundHandlerAdapter extends ChannelInboundHandle
|
||||
final FallbackPacketEncoder newEncoder = new FallbackPacketEncoder(protocolVersion);
|
||||
channel.pipeline().replace(encoder, FALLBACK_PACKET_ENCODER, newEncoder);
|
||||
}
|
||||
closeWith(channel, protocolVersion, packet);
|
||||
ProtocolUtil.closeWith(channel, protocolVersion, packet);
|
||||
} else {
|
||||
channel.close();
|
||||
}
|
||||
|
||||
@@ -34,18 +34,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"})
|
||||
@@ -88,7 +86,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 +99,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 +134,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
-1
@@ -31,7 +31,7 @@ public final class FallbackTailExceptionsHandler extends ChannelDuplexHandler {
|
||||
// Additionally, this will also run after our custom decoder.
|
||||
@Override
|
||||
public void exceptionCaught(final @NotNull ChannelHandlerContext ctx,
|
||||
final @NotNull Throwable cause) throws Exception {
|
||||
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
|
||||
+5
-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,7 +41,7 @@ public final class FallbackVarInt21FrameDecoder extends ByteToMessageDecoder {
|
||||
}
|
||||
|
||||
// Skip any runs of 0x00 we might find
|
||||
final int packetStart = byteBuf.forEachByte(FIND_NON_NUL);
|
||||
final int packetStart = byteBuf.forEachByte(ByteProcessor.FIND_NON_NUL);
|
||||
if (packetStart == -1) {
|
||||
return;
|
||||
}
|
||||
@@ -56,7 +55,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 +74,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);
|
||||
}
|
||||
}
|
||||
+82
-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,7 +401,26 @@ 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(SetHeldItemPacket.class, SetHeldItemPacket::new,
|
||||
map(0x09, MINECRAFT_1_7_2, false),
|
||||
@@ -398,7 +434,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 +450,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 +466,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 +482,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 +493,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 +510,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 +523,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 +538,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 +555,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 +572,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 +589,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 +606,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 +624,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 +638,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
|
||||
|
||||
+79
-49
@@ -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;
|
||||
@@ -95,12 +113,8 @@ public class FallbackPreparer {
|
||||
public FallbackPacket protocolBlacklisted;
|
||||
public FallbackPacket invalidUsername;
|
||||
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));
|
||||
invalidUsername = new FallbackPacketSnapshot(new DisconnectPacket(Sonar.get().getConfig().getVerification().getInvalidUsername(), 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
|
||||
}
|
||||
}
|
||||
+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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+39
-25
@@ -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;
|
||||
}
|
||||
@@ -88,26 +92,17 @@ public final class FallbackGravityHandler extends FallbackVerificationHandler {
|
||||
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 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,48 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
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 +192,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.
|
||||
@@ -193,7 +207,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 +243,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);
|
||||
|
||||
+60
-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,37 @@ 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()) {
|
||||
// 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 +183,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import com.google.gson.internal.LazilyParsedNumber;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.kyori.adventure.nbt.*;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
|
||||
@@ -31,16 +30,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 = ProtocolUtil.PRE_1_20_3_SERIALIZER.serialize(component);
|
||||
this.legacyJson = ProtocolUtil.PRE_1_16_SERIALIZER.serialize(component);
|
||||
this.binaryTag = serialize(new JsonParser().parse(ProtocolUtil.MODERN_SERIALIZER.serialize(component)));
|
||||
}
|
||||
|
||||
// https://github.com/PaperMC/Velocity/blob/dev/3.0.0/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ComponentHolder.java
|
||||
@@ -141,14 +139,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,61 @@ 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 net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.json.JSONOptions;
|
||||
import net.kyori.option.OptionState;
|
||||
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 GsonComponentSerializer PRE_1_16_SERIALIZER = GsonComponentSerializer.builder()
|
||||
.downsampleColors()
|
||||
.options(OptionState.optionState()
|
||||
// before 1.16
|
||||
.value(JSONOptions.EMIT_RGB, Boolean.FALSE)
|
||||
.value(JSONOptions.EMIT_HOVER_EVENT_TYPE, JSONOptions.HoverEventValueMode.LEGACY_ONLY)
|
||||
// before 1.20.3
|
||||
.value(JSONOptions.EMIT_COMPACT_TEXT_COMPONENT, Boolean.FALSE)
|
||||
.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_ID_AS_INT_ARRAY, Boolean.FALSE)
|
||||
.value(JSONOptions.VALIDATE_STRICT_EVENTS, Boolean.FALSE)
|
||||
.build())
|
||||
.build();
|
||||
public static final GsonComponentSerializer PRE_1_20_3_SERIALIZER = GsonComponentSerializer.builder()
|
||||
.options(OptionState.optionState()
|
||||
// after 1.16
|
||||
.value(JSONOptions.EMIT_RGB, Boolean.TRUE)
|
||||
.value(JSONOptions.EMIT_HOVER_EVENT_TYPE, JSONOptions.HoverEventValueMode.MODERN_ONLY)
|
||||
// before 1.20.3
|
||||
.value(JSONOptions.EMIT_COMPACT_TEXT_COMPONENT, Boolean.FALSE)
|
||||
.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_ID_AS_INT_ARRAY, Boolean.FALSE)
|
||||
.value(JSONOptions.VALIDATE_STRICT_EVENTS, Boolean.FALSE)
|
||||
.build())
|
||||
.build();
|
||||
public static final GsonComponentSerializer MODERN_SERIALIZER = GsonComponentSerializer.builder()
|
||||
.options(OptionState.optionState()
|
||||
// after 1.16
|
||||
.value(JSONOptions.EMIT_RGB, Boolean.TRUE)
|
||||
.value(JSONOptions.EMIT_HOVER_EVENT_TYPE, JSONOptions.HoverEventValueMode.MODERN_ONLY)
|
||||
// after 1.20.3
|
||||
.value(JSONOptions.EMIT_COMPACT_TEXT_COMPONENT, Boolean.TRUE)
|
||||
.value(JSONOptions.EMIT_HOVER_SHOW_ENTITY_ID_AS_INT_ARRAY, Boolean.TRUE)
|
||||
.value(JSONOptions.VALIDATE_STRICT_EVENTS, Boolean.TRUE)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
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 +146,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 +259,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 +318,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 +330,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 +358,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
|
||||
|
||||
@@ -159,6 +159,12 @@ verification:
|
||||
# - CREATIVE: 创造模式, 隐藏血量和饱食度
|
||||
# - ADVENTURE: 冒险模式, 所有UI可见
|
||||
gamemode: ADVENTURE
|
||||
# 验证世界的夏令时间
|
||||
# 您可以在这里找到所有的夏令时间:
|
||||
# https://minecraft.wiki/w/Daylight_cycle
|
||||
# 这只是一个可视化设置, 不会改变验证过程!
|
||||
# 如果该值设置为 1000, 则不会发送数据包.
|
||||
time-of-day: 1000
|
||||
# Sonar 缓存它发送到客户端的大部分数据包, 包括 LoginSuccess 数据包.
|
||||
# Sonar 应该使用哪个用户名来接收 LoginSuccess 数据包?
|
||||
# 如果不修改客户端, 用户名将不会对玩家可见
|
||||
@@ -182,7 +188,7 @@ verification:
|
||||
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
|
||||
|
||||
@@ -200,6 +200,7 @@ verification:
|
||||
incorrect: '<prefix><red>Du hast eine falsche Antwort eingegeben! Bitte versuche es erneut.'
|
||||
|
||||
# Nachricht, die dem Spieler angezeigt wird, wenn er überprüft wird
|
||||
# Setzen Sie dies auf '', um die Willkommensnachricht vollständig zu deaktivieren
|
||||
welcome: '<prefix><green>Sie werden gerade überprüft. Bitte bewegen Sie sich nicht, dies ist ein automatischer Vorgang.'
|
||||
|
||||
# Trennungs-Nachricht, die angezeigt wird, wenn jemand beitritt, während der CAPTCHA noch nicht vorbereitet wurde
|
||||
|
||||
@@ -199,6 +199,7 @@ verification:
|
||||
incorrect: '<prefix><red>You have entered a wrong answer! Please try again.'
|
||||
|
||||
# Message shown to the player when they are being verified
|
||||
# Set this to '' to disable the welcome message entirely
|
||||
welcome: '<prefix><green>You are being verified. Please do not move; this is an automatic process.'
|
||||
|
||||
# Disconnect message that is shown when someone joins while the captcha hasn't been prepared yet
|
||||
|
||||
Loaded 100 of 113 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user