mirror of
https://github.com/jonesdevelopment/sonar.git
synced 2026-11-05 17:22:34 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e72e064dcc | ||
|
|
4dcafc9c91 | ||
|
|
7f2e910c2d | ||
|
|
b391ce38fb | ||
|
|
261f8e20e8 | ||
|
|
4c1c509bfa | ||
|
|
a5948bc494 | ||
|
|
de5750e4eb | ||
|
|
71b4bad41c | ||
|
|
05d2e55d69 | ||
|
|
41fe39cf0f | ||
|
|
fa2e68be20 | ||
|
|
2139147952 | ||
|
|
df049a7042 | ||
|
|
8df7874cab | ||
|
|
b132fe839e | ||
|
|
f0bdfa8ef1 | ||
|
|
7899569970 | ||
|
|
16d72fc682 | ||
|
|
425a902ec8 | ||
|
|
fcb52fc6c6 | ||
|
|
ac4d566627 | ||
|
|
0944701797 | ||
|
|
9464bffd8e | ||
|
|
9299edc839 | ||
|
|
41eb6d9767 | ||
|
|
4289b92ab6 | ||
|
|
b2dddfb0a4 | ||
|
|
ce2fca72f0 | ||
|
|
87da706508 | ||
|
|
3480bf9332 | ||
|
|
745d01b4d2 | ||
|
|
4f0d70650b | ||
|
|
2ad0ce11c0 | ||
|
|
fd1e5c9d72 | ||
|
|
e036ce421d | ||
|
|
cf2628a41f | ||
|
|
fcea8e5f11 | ||
|
|
ad5de38b1d | ||
|
|
bc0661c10c | ||
|
|
5e233b4f73 | ||
|
|
e7f816fcaa | ||
|
|
d87b775b76 | ||
|
|
b973f70441 | ||
|
|
8d6a45c3b4 | ||
|
|
142ce80b9b | ||
|
|
a8589e36e2 | ||
|
|
26bee1ca0b | ||
|
|
9cfeb59604 | ||
|
|
8464da8ef2 | ||
|
|
5fa0814329 | ||
|
|
59332b8134 | ||
|
|
3ca1852e03 | ||
|
|
625a3a1e8e | ||
|
|
646f8f08f6 | ||
|
|
947eb7f6a1 | ||
|
|
881f826692 | ||
|
|
4252362584 | ||
|
|
7271a2a13f | ||
|
|
4343c0e0be | ||
|
|
f42cade9cc | ||
|
|
009dd08cb4 | ||
|
|
2136da30fa | ||
|
|
c63b19f42b | ||
|
|
9943b611ad | ||
|
|
f151230f44 | ||
|
|
797b6a6d27 | ||
|
|
dc4b9f5be0 | ||
|
|
2c9bc89b7d | ||
|
|
6c1a56d59f | ||
|
|
aa986e2c01 | ||
|
|
3bcd5e5d34 | ||
|
|
341e647526 | ||
|
|
d7f48ecdf0 | ||
|
|
2c82047ac3 | ||
|
|
d03b8ff195 | ||
|
|
183bbc0019 | ||
|
|
31282d81c4 | ||
|
|
f0c143fadf | ||
|
|
6be001ede8 | ||
|
|
8044b92fb8 | ||
|
|
81b83b5123 | ||
|
|
a0c054e0a4 | ||
|
|
a668073d17 | ||
|
|
2653754590 | ||
|
|
7a1d22d922 | ||
|
|
f04703405d | ||
|
|
add066ea0e | ||
|
|
6bc2b1cb82 | ||
|
|
41e43bcba6 | ||
|
|
d7f150bf6d | ||
|
|
37101c665d | ||
|
|
4b3e88fcda | ||
|
|
b10a04294d | ||
|
|
0b06b59f3a | ||
|
|
a09beb9309 | ||
|
|
d67b61fa35 | ||
|
|
57490a7740 | ||
|
|
834fd2e609 | ||
|
|
3c3eeb742c | ||
|
|
445db48925 | ||
|
|
f2667390cc | ||
|
|
2e849bf952 | ||
|
|
275e0009a4 | ||
|
|
16802e8002 | ||
|
|
c3d85a9c4a | ||
|
|
39686177a1 | ||
|
|
3204f62be6 | ||
|
|
73ddcc9f03 | ||
|
|
13cbb5ae1d | ||
|
|
a953df33d9 | ||
|
|
af12ef535c | ||
|
|
7de08ffa98 | ||
|
|
c56d41cd77 | ||
|
|
4b30729138 | ||
|
|
1254514ad2 | ||
|
|
b4673d8e9f | ||
|
|
5eaaf88ec8 | ||
|
|
367ce07cb0 |
No files matched your search
@@ -26,6 +26,12 @@
|
||||
[License](https://github.com/jonesdevelopment/sonar/?tab=readme-ov-file#license)
|
||||
</div>
|
||||
|
||||
> ### Notice about Sonar 3.0
|
||||
>
|
||||
> We're excited to announce the public release of Sonar 3.0.
|
||||
> If you are interested, please check out https://sonar.top/. You can read more about what sets Sonar 3.0 apart from Sonar 2.0 [here](<https://docs.sonar.top/faq>).
|
||||
> If you want more robust protection, better performance, and awesome new features, check out Sonar 3.0!
|
||||
|
||||
## Design and Goal
|
||||
* Effective, lightweight, and easy-to-use
|
||||
* No unnecessary features and clean code
|
||||
@@ -51,7 +57,12 @@ If you or one of your players experiences a false positive, make sure to report
|
||||
## Sponsors
|
||||
Massive thanks to the sponsors of Sonar who help keep this project running:
|
||||
|
||||
<a href="https://github.com/Chumb3x"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/79834154?v=4&h=50&w=50&fit=cover&mask=circle&maxage=7d" alt="logo" align="center"></a>
|
||||
|
||||
### Past Sponsors
|
||||
|
||||
<a href="https://github.com/Hydoxl"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/107579333?v=4&h=50&w=50&fit=cover&mask=circle&maxage=7d" alt="logo" align="center"></a>
|
||||
<a href="https://github.com/ItzErpandX"><img src="https://images.weserv.nl/?url=avatars.githubusercontent.com/u/84748484?v=4?s=400?v=4&h=50&w=50&fit=cover&mask=circle&maxage=7d" alt="logo" align="center"></a>
|
||||
|
||||
## Building
|
||||
If you want to build your own version of Sonar, please take a look at the [Sonar building documentation](https://docs.jonesdev.xyz/development/building).
|
||||
|
||||
@@ -29,10 +29,11 @@ jobs:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
|
||||
- name: Build with Gradle
|
||||
uses: gradle/gradle-build-action@v3
|
||||
with:
|
||||
arguments: build-sonar
|
||||
run: ./gradlew build-sonar
|
||||
|
||||
- name: Upload Bukkit Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
@@ -21,11 +21,11 @@ import net.kyori.adventure.audience.Audience;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import xyz.jonesdev.sonar.api.antibot.AntiBot;
|
||||
import xyz.jonesdev.sonar.api.command.subcommand.SubcommandRegistry;
|
||||
import xyz.jonesdev.sonar.api.config.SonarConfiguration;
|
||||
import xyz.jonesdev.sonar.api.database.controller.VerifiedPlayerController;
|
||||
import xyz.jonesdev.sonar.api.event.SonarEventManager;
|
||||
import xyz.jonesdev.sonar.api.fallback.Fallback;
|
||||
import xyz.jonesdev.sonar.api.logger.LoggerWrapper;
|
||||
import xyz.jonesdev.sonar.api.notification.ActionBarNotificationHandler;
|
||||
import xyz.jonesdev.sonar.api.notification.ChatNotificationHandler;
|
||||
@@ -152,12 +152,12 @@ public interface Sonar {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the anti-bot handler (called "Fallback") for Sonar.
|
||||
* Gets the anti-bot handler for Sonar.
|
||||
*
|
||||
* @return The {@link Fallback} instance.
|
||||
* @return The {@link AntiBot} instance.
|
||||
*/
|
||||
@NotNull default Fallback getFallback() {
|
||||
return Fallback.INSTANCE;
|
||||
@NotNull default AntiBot getAntiBot() {
|
||||
return AntiBot.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+6
-6
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.api.fallback;
|
||||
package xyz.jonesdev.sonar.api.antibot;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import lombok.AccessLevel;
|
||||
@@ -23,9 +23,9 @@ import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.antibot.captcha.CaptchaGenerator;
|
||||
import xyz.jonesdev.sonar.api.antibot.ratelimit.Ratelimiter;
|
||||
import xyz.jonesdev.sonar.api.config.SonarConfiguration;
|
||||
import xyz.jonesdev.sonar.api.fallback.captcha.CaptchaGenerator;
|
||||
import xyz.jonesdev.sonar.api.fallback.ratelimit.Ratelimiter;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -33,12 +33,12 @@ import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
@Getter
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class Fallback {
|
||||
public static final Fallback INSTANCE = new Fallback();
|
||||
public final class AntiBot {
|
||||
public static final AntiBot INSTANCE = new AntiBot();
|
||||
|
||||
private final ConcurrentMap<InetAddress, Integer> online = new ConcurrentHashMap<>(128);
|
||||
private final ConcurrentMap<InetAddress, Boolean> connected = new ConcurrentHashMap<>(256);
|
||||
private final FallbackLoginQueue queue = new FallbackLoginQueue();
|
||||
private final LoginQueue queue = new LoginQueue();
|
||||
@Setter
|
||||
private Cache<String, Integer> blacklist;
|
||||
@Setter
|
||||
+12
-12
@@ -15,17 +15,17 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.api.fallback;
|
||||
package xyz.jonesdev.sonar.api.antibot;
|
||||
|
||||
public interface FallbackPipelines {
|
||||
String FALLBACK_INACTIVE_LISTENER = "sonar-inactive-listener";
|
||||
String FALLBACK_INBOUND_HANDLER = "sonar-inbound-handler";
|
||||
String FALLBACK_FRAME_DECODER = "sonar-frame-decoder";
|
||||
String FALLBACK_FRAME_ENCODER = "sonar-frame-encoder";
|
||||
String FALLBACK_TIMEOUT = "sonar-timeout";
|
||||
String FALLBACK_PACKET_HANDLER = "sonar-packet-handler";
|
||||
String FALLBACK_PACKET_ENCODER = "sonar-packet-encoder";
|
||||
String FALLBACK_PACKET_DECODER = "sonar-packet-decoder";
|
||||
String FALLBACK_TAIL_EXCEPTIONS = "sonar-exception-tail";
|
||||
String FALLBACK_BANDWIDTH = "sonar-bandwidth-counter";
|
||||
public interface ChannelPipelines {
|
||||
String SONAR_INACTIVE_LISTENER = "sonar-inactive-listener";
|
||||
String SONAR_INBOUND_HANDLER = "sonar-inbound-handler";
|
||||
String SONAR_FRAME_DECODER = "sonar-frame-decoder";
|
||||
String SONAR_FRAME_ENCODER = "sonar-frame-encoder";
|
||||
String SONAR_TIMEOUT = "sonar-timeout";
|
||||
String SONAR_PACKET_HANDLER = "sonar-packet-handler";
|
||||
String SONAR_PACKET_ENCODER = "sonar-packet-encoder";
|
||||
String SONAR_PACKET_DECODER = "sonar-packet-decoder";
|
||||
String SONAR_TAIL_EXCEPTIONS = "sonar-exception-tail";
|
||||
String SONAR_BANDWIDTH = "sonar-bandwidth-counter";
|
||||
}
|
||||
+2
-2
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.api.fallback;
|
||||
package xyz.jonesdev.sonar.api.antibot;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
@@ -30,7 +30,7 @@ import java.util.concurrent.Executors;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
public final class FallbackLoginQueue {
|
||||
public final class LoginQueue {
|
||||
// Fixed thread pool executor for all new verifications
|
||||
private static final int THREAD_POOL_SIZE = Math.min(0x7fff, Runtime.getRuntime().availableProcessors() * 2);
|
||||
private static final ExecutorService QUEUE_EXECUTOR = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
|
||||
+3
-3
@@ -15,18 +15,18 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.api.fallback;
|
||||
package xyz.jonesdev.sonar.api.antibot;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.api.timer.SystemTimer;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
public interface FallbackUser {
|
||||
public interface SonarUser {
|
||||
@NotNull Channel channel();
|
||||
|
||||
@NotNull InetAddress getInetAddress();
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.api.fallback.captcha;
|
||||
package xyz.jonesdev.sonar.api.antibot.captcha;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
+7
-2
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.api.fallback.protocol;
|
||||
package xyz.jonesdev.sonar.api.antibot.protocol;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -70,7 +70,12 @@ public enum ProtocolVersion {
|
||||
MINECRAFT_1_20_5(766, "1.20.5"),
|
||||
MINECRAFT_1_21(767, "1.21"),
|
||||
MINECRAFT_1_21_2(768, "1.21.2"),
|
||||
MINECRAFT_1_21_4(769, "1.21.4");
|
||||
MINECRAFT_1_21_4(769, "1.21.4"),
|
||||
MINECRAFT_1_21_5(770, "1.21.5"),
|
||||
MINECRAFT_1_21_6(771, "1.21.6"),
|
||||
MINECRAFT_1_21_7(772, "1.21.7"),
|
||||
MINECRAFT_1_21_9(773, "1.21.9"),
|
||||
MINECRAFT_1_21_11(774, "1.21.11");
|
||||
|
||||
private final int protocol;
|
||||
private final String name;
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.api.fallback.ratelimit;
|
||||
package xyz.jonesdev.sonar.api.antibot.ratelimit;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -30,6 +30,7 @@ import xyz.jonesdev.sonar.api.command.SonarCommand;
|
||||
import xyz.jonesdev.sonar.api.database.ormlite.H2DatabaseTypeAdapter;
|
||||
import xyz.jonesdev.sonar.api.database.ormlite.MariaDbDatabaseTypeAdapter;
|
||||
import xyz.jonesdev.sonar.api.database.ormlite.MysqlDatabaseTypeAdapter;
|
||||
import xyz.jonesdev.sonar.api.database.ormlite.PostgresDatabaseTypeAdapter;
|
||||
import xyz.jonesdev.sonar.api.webhook.DiscordWebhook;
|
||||
|
||||
import java.io.File;
|
||||
@@ -139,7 +140,7 @@ public final class SonarConfiguration {
|
||||
|
||||
// General settings
|
||||
logPlayerAddresses = generalConfig.getBoolean("general.log-player-addresses");
|
||||
maxOnlinePerIp = clamp(generalConfig.getInt("general.max-online-per-ip"), 0, 99);
|
||||
maxOnlinePerIp = clamp(generalConfig.getInt("general.max-online-per-ip"), -1, 99);
|
||||
|
||||
// Attack tracker
|
||||
minPlayersForAttack = clamp(generalConfig.getInt("attack-tracker.min-players-for-attack"), 2, 1024);
|
||||
@@ -177,7 +178,7 @@ public final class SonarConfiguration {
|
||||
verification.map.timing = Verification.Timing.valueOf(generalConfig.getString("verification.checks.map-captcha.timing"));
|
||||
verification.map.precomputeAmount = clamp(generalConfig.getInt("verification.checks.map-captcha.precompute"), 10, 5000);
|
||||
verification.map.maxDuration = clamp(generalConfig.getInt("verification.checks.map-captcha.max-duration"), 5000, 360000);
|
||||
verification.map.maxTries = generalConfig.getInt("verification.checks.map-captcha.max-tries");
|
||||
verification.map.maxTries = clamp(generalConfig.getInt("verification.checks.map-captcha.max-tries"), 1, 100);
|
||||
verification.map.alphabet = generalConfig.getString("verification.checks.map-captcha.alphabet");
|
||||
verification.map.backgroundImage = null;
|
||||
|
||||
@@ -198,7 +199,6 @@ public final class SonarConfiguration {
|
||||
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"));
|
||||
|
||||
verification.checkGeyser = generalConfig.getBoolean("verification.check-geyser-players");
|
||||
verification.logConnections = generalConfig.getBoolean("verification.log-connections");
|
||||
@@ -206,6 +206,7 @@ public final class SonarConfiguration {
|
||||
verification.debugXYZPositions = generalConfig.getBoolean("verification.debug-xyz-positions");
|
||||
verification.readTimeout = clamp(generalConfig.getInt("verification.read-timeout"), 1000, 30000);
|
||||
verification.writeTimeout = clamp(generalConfig.getInt("verification.write-timeout"), 1000, 30000);
|
||||
verification.maxPacketCount = clamp(generalConfig.getInt("verification.max-packets"), 200, 9999);
|
||||
verification.reconnectDelay = clamp(generalConfig.getInt("verification.rejoin-delay"), 0, 100000);
|
||||
verification.rememberTime = clamp(generalConfig.getInt("verification.remember-time"), 0, 86400000);
|
||||
verification.blacklistTime = clamp(generalConfig.getInt("verification.blacklist-time"), 0, 86400000);
|
||||
@@ -418,10 +419,10 @@ public final class SonarConfiguration {
|
||||
private boolean logDuringAttack;
|
||||
private boolean debugXYZPositions;
|
||||
private Pattern validNameRegex;
|
||||
private Pattern validLocaleRegex;
|
||||
|
||||
private int readTimeout;
|
||||
private int writeTimeout;
|
||||
private int maxPacketCount;
|
||||
private int reconnectDelay;
|
||||
private int rememberTime;
|
||||
private int blacklistTime;
|
||||
@@ -447,14 +448,14 @@ public final class SonarConfiguration {
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum Type {
|
||||
MYSQL("MySQL", "jdbc:mysql://%s:%d/%s", new MysqlDatabaseTypeAdapter(),
|
||||
MYSQL("MySQL", "jdbc:mysql://%s:%d/%s%s", new MysqlDatabaseTypeAdapter(),
|
||||
Library.builder()
|
||||
.groupId("com{}mysql")
|
||||
.artifactId("mysql-connector-j")
|
||||
.version("9.0.0")
|
||||
.relocate("com{}mysql", "xyz{}jonesdev{}sonar{}libs{}mysql")
|
||||
.build()),
|
||||
MARIADB("MariaDB", "jdbc:mariadb://%s:%d/%s", new MariaDbDatabaseTypeAdapter(),
|
||||
MARIADB("MariaDB", "jdbc:mariadb://%s:%d/%s%s", new MariaDbDatabaseTypeAdapter(),
|
||||
Library.builder()
|
||||
.groupId("org{}mariadb{}jdbc")
|
||||
.artifactId("mariadb-java-client")
|
||||
@@ -468,6 +469,13 @@ public final class SonarConfiguration {
|
||||
.version("2.2.220")
|
||||
.relocate("org{}h2", "xyz{}jonesdev{}sonar{}libs{}h2")
|
||||
.build()),
|
||||
POSTGRESQL("PostgreSQL", "jdbc:postgresql://%s:%d/%s%s", new PostgresDatabaseTypeAdapter(),
|
||||
Library.builder()
|
||||
.groupId("org{}postgresql")
|
||||
.artifactId("postgresql")
|
||||
.version("42.7.8")
|
||||
.relocate("org{}postgresql", "xyz{}jonesdev{}sonar{}libs{}postgresql")
|
||||
.build()),
|
||||
NONE("None", null, null, null);
|
||||
|
||||
private final String displayName;
|
||||
|
||||
+11
-3
@@ -54,6 +54,8 @@ public final class VerifiedPlayerController {
|
||||
@Getter
|
||||
private final @NotNull SonarConfiguration.Database.Type cachedDatabaseType;
|
||||
private final ExecutorService updateService = Executors.newSingleThreadExecutor();
|
||||
@Getter
|
||||
private int maximumAge;
|
||||
|
||||
public VerifiedPlayerController(final @NotNull LibraryManager libraryManager) {
|
||||
final SonarConfiguration.Database database = Sonar.get0().getConfig().getDatabase();
|
||||
@@ -87,7 +89,8 @@ public final class VerifiedPlayerController {
|
||||
jdbcURL = String.format(cachedDatabaseType.getConnectionString(),
|
||||
Sonar.get0().getConfig().getGeneralConfig().getString("database.host"),
|
||||
Sonar.get0().getConfig().getGeneralConfig().getInt("database.port"),
|
||||
Sonar.get0().getConfig().getGeneralConfig().getString("database.name"));
|
||||
Sonar.get0().getConfig().getGeneralConfig().getString("database.name"),
|
||||
"?autoReconnect=true");
|
||||
}
|
||||
|
||||
connectionSource = new JdbcConnectionSource(jdbcURL,
|
||||
@@ -107,6 +110,7 @@ public final class VerifiedPlayerController {
|
||||
|
||||
dao = DaoManager.createDao(connectionSource, VerifiedPlayer.class);
|
||||
queryBuilder = dao.queryBuilder();
|
||||
maximumAge = database.getMaximumAge();
|
||||
|
||||
// Make sure to run the clean task and the caching task in the same thread
|
||||
// https://github.com/jonesdevelopment/sonar/issues/150
|
||||
@@ -114,7 +118,7 @@ public final class VerifiedPlayerController {
|
||||
if (connectionSource != null) {
|
||||
try {
|
||||
// Make sure to clear all outdated entries first
|
||||
clearOld(database.getMaximumAge());
|
||||
clearOld(maximumAge, false);
|
||||
// Add all entries from the database to the cache
|
||||
dao.queryForAll().forEach(verifiedPlayer -> cache.add(verifiedPlayer.getFingerprint()));
|
||||
} catch (SQLException exception) {
|
||||
@@ -148,7 +152,8 @@ public final class VerifiedPlayerController {
|
||||
/**
|
||||
* Clear all old entries using the given timestamp
|
||||
*/
|
||||
private void clearOld(final @Range(from = 1, to = 365) int maximumAge) throws SQLException {
|
||||
public void clearOld(final @Range(from = 1, to = 365) int maximumAge,
|
||||
final boolean removeExisting) throws SQLException {
|
||||
final long timestamp = Instant.now().minus(maximumAge, ChronoUnit.DAYS).getEpochSecond() * 1000L;
|
||||
|
||||
final List<VerifiedPlayer> oldEntries = queryBuilder.where()
|
||||
@@ -158,6 +163,9 @@ public final class VerifiedPlayerController {
|
||||
if (oldEntries != null && !oldEntries.isEmpty()) {
|
||||
for (final VerifiedPlayer player : oldEntries) {
|
||||
dao.delete(player);
|
||||
if (removeExisting) {
|
||||
cache.remove(player.getFingerprint());
|
||||
}
|
||||
}
|
||||
Sonar.get0().getLogger().info("Removed {} database entries older than {} days.",
|
||||
oldEntries.size(), maximumAge);
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package xyz.jonesdev.sonar.api.database.ormlite;
|
||||
|
||||
import com.j256.ormlite.jdbc.db.PostgresDatabaseType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public final class PostgresDatabaseTypeAdapter extends PostgresDatabaseType {
|
||||
|
||||
// We need to override the default driver class name
|
||||
// to use the custom relocated Postgres driver
|
||||
@Override
|
||||
protected String @NotNull [] getDriverClassNames() {
|
||||
return new String[]{"xyz.jonesdev.sonar.libs.postgresql.Driver"};
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -20,8 +20,8 @@ package xyz.jonesdev.sonar.api.event.impl;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import xyz.jonesdev.sonar.api.antibot.captcha.CaptchaGenerator;
|
||||
import xyz.jonesdev.sonar.api.event.SonarEvent;
|
||||
import xyz.jonesdev.sonar.api.fallback.captcha.CaptchaGenerator;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
|
||||
@@ -20,12 +20,12 @@ package xyz.jonesdev.sonar.api.event.impl;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import xyz.jonesdev.sonar.api.antibot.SonarUser;
|
||||
import xyz.jonesdev.sonar.api.event.SonarEvent;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@RequiredArgsConstructor
|
||||
public final class UserBlacklistedEvent implements SonarEvent {
|
||||
private final FallbackUser user;
|
||||
private final SonarUser user;
|
||||
}
|
||||
@@ -21,13 +21,13 @@ import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.antibot.SonarUser;
|
||||
import xyz.jonesdev.sonar.api.event.SonarEvent;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@RequiredArgsConstructor
|
||||
public final class UserVerifyFailedEvent implements SonarEvent {
|
||||
private final @NotNull FallbackUser user;
|
||||
private final @NotNull SonarUser user;
|
||||
private final @NotNull String reason;
|
||||
}
|
||||
@@ -20,12 +20,12 @@ package xyz.jonesdev.sonar.api.event.impl;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import xyz.jonesdev.sonar.api.antibot.SonarUser;
|
||||
import xyz.jonesdev.sonar.api.event.SonarEvent;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@RequiredArgsConstructor
|
||||
public final class UserVerifyJoinEvent implements SonarEvent {
|
||||
private final FallbackUser user;
|
||||
private final SonarUser user;
|
||||
}
|
||||
@@ -20,12 +20,12 @@ package xyz.jonesdev.sonar.api.event.impl;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import xyz.jonesdev.sonar.api.antibot.SonarUser;
|
||||
import xyz.jonesdev.sonar.api.event.SonarEvent;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@RequiredArgsConstructor
|
||||
public final class UserVerifySuccessEvent implements SonarEvent {
|
||||
private final FallbackUser user;
|
||||
private final SonarUser user;
|
||||
}
|
||||
+7
-5
@@ -33,8 +33,8 @@ import static xyz.jonesdev.sonar.api.profiler.SimpleProcessProfiler.*;
|
||||
import static xyz.jonesdev.sonar.api.timer.SystemTimer.DATE_FORMATTER;
|
||||
|
||||
@Getter
|
||||
public final class ActionBarNotificationHandler extends NotificationHandler {
|
||||
private int animationIndex;
|
||||
public class ActionBarNotificationHandler extends NotificationHandler {
|
||||
protected int animationIndex;
|
||||
|
||||
@Override
|
||||
public void handleNotification() {
|
||||
@@ -54,9 +54,11 @@ public final class ActionBarNotificationHandler extends NotificationHandler {
|
||||
Placeholder.component("prefix", Sonar.get0().getConfig().getPrefix()),
|
||||
Placeholder.unparsed("attack-duration", attackDuration),
|
||||
Placeholder.unparsed("animation", nextAnimation()),
|
||||
Placeholder.unparsed("queued", DECIMAL_FORMAT.format(Sonar.get0().getFallback().getQueue().getPlayers().size())),
|
||||
Placeholder.unparsed("verifying", DECIMAL_FORMAT.format(Sonar.get0().getFallback().getConnected().size())),
|
||||
Placeholder.unparsed("blacklisted", DECIMAL_FORMAT.format(Sonar.get0().getFallback().getBlacklist().estimatedSize())),
|
||||
Placeholder.unparsed("process-cpu", Sonar.DECIMAL_FORMAT.format(getProcessCPUUsage())),
|
||||
Placeholder.unparsed("system-cpu", Sonar.DECIMAL_FORMAT.format(getSystemCPUUsage())),
|
||||
Placeholder.unparsed("queued", DECIMAL_FORMAT.format(Sonar.get0().getAntiBot().getQueue().getPlayers().size())),
|
||||
Placeholder.unparsed("verifying", DECIMAL_FORMAT.format(Sonar.get0().getAntiBot().getConnected().size())),
|
||||
Placeholder.unparsed("blacklisted", DECIMAL_FORMAT.format(Sonar.get0().getAntiBot().getBlacklist().estimatedSize())),
|
||||
Placeholder.unparsed("total-joins", DECIMAL_FORMAT.format(Sonar.get0().getStatistics().getTotalPlayersJoined())),
|
||||
Placeholder.unparsed("logins-per-second", DECIMAL_FORMAT.format(Sonar.get0().getStatistics().getLoginsPerSecond())),
|
||||
Placeholder.unparsed("connections-per-second", DECIMAL_FORMAT.format(Sonar.get0().getStatistics().getConnectionsPerSecond())),
|
||||
|
||||
@@ -54,9 +54,9 @@ public final class ChatNotificationHandler extends NotificationHandler {
|
||||
for (int i = 0; i < chatNotificationComponents.length; i++) {
|
||||
chatNotificationComponents[i] = MiniMessage.miniMessage().deserialize(chatNotification.get(i),
|
||||
Placeholder.component("prefix", Sonar.get0().getConfig().getPrefix()),
|
||||
Placeholder.unparsed("queued", DECIMAL_FORMAT.format(Sonar.get0().getFallback().getQueue().getPlayers().size())),
|
||||
Placeholder.unparsed("verifying", DECIMAL_FORMAT.format(Sonar.get0().getFallback().getConnected().size())),
|
||||
Placeholder.unparsed("blacklisted", DECIMAL_FORMAT.format(Sonar.get0().getFallback().getBlacklist().estimatedSize())),
|
||||
Placeholder.unparsed("queued", DECIMAL_FORMAT.format(Sonar.get0().getAntiBot().getQueue().getPlayers().size())),
|
||||
Placeholder.unparsed("verifying", DECIMAL_FORMAT.format(Sonar.get0().getAntiBot().getConnected().size())),
|
||||
Placeholder.unparsed("blacklisted", DECIMAL_FORMAT.format(Sonar.get0().getAntiBot().getBlacklist().estimatedSize())),
|
||||
Placeholder.unparsed("total-joins", DECIMAL_FORMAT.format(Sonar.get0().getStatistics().getTotalPlayersJoined())),
|
||||
Placeholder.unparsed("logins-per-second", DECIMAL_FORMAT.format(Sonar.get0().getStatistics().getLoginsPerSecond())),
|
||||
Placeholder.unparsed("connections-per-second", DECIMAL_FORMAT.format(Sonar.get0().getStatistics().getConnectionsPerSecond())),
|
||||
|
||||
@@ -53,8 +53,8 @@ public final class AttackTracker {
|
||||
|
||||
public void checkIfUnderAttack() {
|
||||
final long joinsPerSecond = Sonar.get0().getStatistics().getLoginsPerSecond();
|
||||
final int verifyingPlayers = Sonar.get0().getFallback().getConnected().size();
|
||||
final int queuedPlayers = Sonar.get0().getFallback().getQueue().getPlayers().size();
|
||||
final int verifyingPlayers = Sonar.get0().getAntiBot().getConnected().size();
|
||||
final int queuedPlayers = Sonar.get0().getAntiBot().getQueue().getPlayers().size();
|
||||
final int minPlayers = Sonar.get0().getConfig().getMinPlayersForAttack();
|
||||
|
||||
if (joinsPerSecond > minPlayers // Check the number of bots/joins per second.
|
||||
@@ -116,7 +116,7 @@ public final class AttackTracker {
|
||||
final String formattedDuration = String.format("%d minutes, %.0f seconds", minutes, seconds);
|
||||
final String startTimestamp = String.valueOf(currentAttack.duration.getStart() / 1000L);
|
||||
final String endTimestamp = String.valueOf(System.currentTimeMillis() / 1000L);
|
||||
final long blacklisted = Sonar.get0().getFallback().getBlacklist().estimatedSize();
|
||||
final long blacklisted = Sonar.get0().getAntiBot().getBlacklist().estimatedSize();
|
||||
// Calculate during-attack-statistics using their deltas
|
||||
final long totalVerified = Sonar.get0().getVerifiedPlayerController().getCache().size();
|
||||
final long verified = Math.max(totalVerified - currentAttack.successfulVerifications, 0);
|
||||
|
||||
@@ -65,6 +65,7 @@ public class UpdateChecker {
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
Sonar.get0().getLogger().warn("Unable to retrieve version information: {}", throwable);
|
||||
throwable.printStackTrace(System.err);
|
||||
lastCheckResult = CheckResult.API_ERROR;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -62,6 +62,7 @@ allprojects {
|
||||
relocate("org.mariadb", "xyz.jonesdev.sonar.libs.mariadb")
|
||||
relocate("org.h2", "xyz.jonesdev.sonar.libs.h2")
|
||||
relocate("com.jhlabs", "xyz.jonesdev.sonar.libs.jhlabs")
|
||||
relocate("org.postgresql", "xyz.jonesdev.sonar.libs.postgresql")
|
||||
|
||||
// Exclude unnecessary metadata information
|
||||
exclude("META-INF/*/**")
|
||||
|
||||
@@ -28,8 +28,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import xyz.jonesdev.sonar.api.SonarPlatform;
|
||||
import xyz.jonesdev.sonar.api.logger.LoggerWrapper;
|
||||
import xyz.jonesdev.sonar.bukkit.antibot.BukkitInjector;
|
||||
import xyz.jonesdev.sonar.bukkit.command.BukkitSonarCommand;
|
||||
import xyz.jonesdev.sonar.bukkit.fallback.FallbackBukkitInjector;
|
||||
import xyz.jonesdev.sonar.bukkit.listener.BukkitJoinListener;
|
||||
import xyz.jonesdev.sonar.common.boot.SonarBootstrap;
|
||||
|
||||
@@ -108,8 +108,8 @@ public final class SonarBukkit extends SonarBootstrap<SonarBukkitPlugin> {
|
||||
Objects.requireNonNull(getPlugin().getCommand("sonar")).setExecutor(new BukkitSonarCommand());
|
||||
|
||||
// Try to inject into the server
|
||||
if (FallbackBukkitInjector.isLateBindEnabled()) {
|
||||
getPlugin().getServer().getScheduler().runTask(getPlugin(), FallbackBukkitInjector::inject);
|
||||
if (BukkitInjector.isLateBindEnabled()) {
|
||||
getPlugin().getServer().getScheduler().runTask(getPlugin(), BukkitInjector::inject);
|
||||
} else {
|
||||
getPlugin().getServer().getPluginManager().registerEvents(new BukkitJoinListener(), getPlugin());
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
package xyz.jonesdev.sonar.bukkit;
|
||||
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import xyz.jonesdev.sonar.bukkit.fallback.FallbackBukkitInjector;
|
||||
import xyz.jonesdev.sonar.bukkit.antibot.BukkitInjector;
|
||||
|
||||
public final class SonarBukkitPlugin extends JavaPlugin {
|
||||
private SonarBukkit bootstrap;
|
||||
@@ -26,8 +26,8 @@ public final class SonarBukkitPlugin extends JavaPlugin {
|
||||
@Override
|
||||
public void onLoad() {
|
||||
// Inject early if late-bind is disabled
|
||||
if (!FallbackBukkitInjector.isLateBindEnabled()) {
|
||||
FallbackBukkitInjector.inject();
|
||||
if (!BukkitInjector.isLateBindEnabled()) {
|
||||
BukkitInjector.inject();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+24
-21
@@ -15,37 +15,37 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.bukkit.fallback;
|
||||
package xyz.jonesdev.sonar.bukkit.antibot;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.FallbackInboundHandlerAdapter;
|
||||
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.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.InboundHandlerAdapter;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacketRegistry;
|
||||
import xyz.jonesdev.sonar.common.protocol.packets.handshake.HandshakePacket;
|
||||
import xyz.jonesdev.sonar.common.protocol.packets.login.LoginStartPacket;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
import xyz.jonesdev.sonar.common.util.exception.QuietDecoderException;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
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.api.antibot.ChannelPipelines.SONAR_INBOUND_HANDLER;
|
||||
import static xyz.jonesdev.sonar.common.protocol.packets.handshake.HandshakePacket.*;
|
||||
|
||||
final class FallbackBukkitInboundHandler extends FallbackInboundHandlerAdapter {
|
||||
final class BukkitInboundHandler extends InboundHandlerAdapter {
|
||||
private static final ProtocolVersion DEFAULT_PROTOCOL_VERSION = ProtocolVersion.MINECRAFT_1_7_2;
|
||||
|
||||
private FallbackPacketRegistry.ProtocolRegistry registry;
|
||||
private SonarPacketRegistry.ProtocolRegistry registry;
|
||||
|
||||
FallbackBukkitInboundHandler() {
|
||||
updateRegistry(FallbackPacketRegistry.HANDSHAKE, DEFAULT_PROTOCOL_VERSION);
|
||||
BukkitInboundHandler() {
|
||||
updateRegistry(SonarPacketRegistry.HANDSHAKE, DEFAULT_PROTOCOL_VERSION);
|
||||
|
||||
channelRemovalListener = (pipeline, name, handler) -> {
|
||||
super.channelRemovalListener = (pipeline, name, handler) -> {
|
||||
final ChannelInactiveListener inactiveListener = pipeline.get(ChannelInactiveListener.class);
|
||||
|
||||
if (inactiveListener != null) {
|
||||
@@ -54,9 +54,9 @@ final class FallbackBukkitInboundHandler extends FallbackInboundHandlerAdapter {
|
||||
};
|
||||
}
|
||||
|
||||
public void updateRegistry(final @NotNull FallbackPacketRegistry registry,
|
||||
public void updateRegistry(final @NotNull SonarPacketRegistry registry,
|
||||
final @NotNull ProtocolVersion protocolVersion) {
|
||||
this.registry = registry.getProtocolRegistry(FallbackPacketRegistry.Direction.SERVERBOUND, protocolVersion);
|
||||
this.registry = registry.getProtocolRegistry(SonarPacketRegistry.Direction.SERVERBOUND, protocolVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,7 +79,7 @@ final class FallbackBukkitInboundHandler extends FallbackInboundHandlerAdapter {
|
||||
}
|
||||
|
||||
final int packetId = ProtocolUtil.readVarInt(byteBuf);
|
||||
final FallbackPacket packet = registry.createPacket(packetId);
|
||||
final SonarPacket packet = registry.createPacket(packetId);
|
||||
|
||||
// Skip the packet if it's not registered within Sonar's packet registry
|
||||
if (packet == null) {
|
||||
@@ -119,9 +119,10 @@ final class FallbackBukkitInboundHandler extends FallbackInboundHandlerAdapter {
|
||||
// Let the actual handler know about the handshake packet
|
||||
handleHandshake(ctx, handshake.getHostname(), handshake.getProtocolVersionId());
|
||||
// Be ready for the next packet (which is supposed to be a login packet)
|
||||
updateRegistry(FallbackPacketRegistry.LOGIN, Objects.requireNonNull(protocolVersion));
|
||||
updateRegistry(SonarPacketRegistry.LOGIN, Objects.requireNonNull(protocolVersion));
|
||||
break;
|
||||
default:
|
||||
byteBuf.release();
|
||||
throw ProtocolUtil.DEBUG ? new DecoderException("Bad handshake intent " + handshake.getIntent())
|
||||
: QuietDecoderException.INSTANCE;
|
||||
}
|
||||
@@ -135,9 +136,9 @@ final class FallbackBukkitInboundHandler extends FallbackInboundHandlerAdapter {
|
||||
byteBuf.readerIndex(originalReaderIndex);
|
||||
ctx.fireChannelRead(byteBuf.retain());
|
||||
// TODO: recode this?
|
||||
final ChannelHandler inboundHandler = ctx.pipeline().remove(FALLBACK_INBOUND_HANDLER);
|
||||
if (inboundHandler != null) {
|
||||
channelRemovalListener.accept(ctx.pipeline(), FALLBACK_INBOUND_HANDLER, inboundHandler);
|
||||
final ChannelHandler inboundHandler = ctx.pipeline().remove(SONAR_INBOUND_HANDLER);
|
||||
if (inboundHandler != null && channelRemovalListener != null) {
|
||||
channelRemovalListener.accept(ctx.pipeline(), SONAR_INBOUND_HANDLER, inboundHandler);
|
||||
}
|
||||
}, loginStart.getUsername(), socketAddress);
|
||||
byteBuf.release();
|
||||
@@ -146,6 +147,8 @@ final class FallbackBukkitInboundHandler extends FallbackInboundHandlerAdapter {
|
||||
|
||||
byteBuf.readerIndex(originalReaderIndex);
|
||||
ctx.fireChannelRead(byteBuf);
|
||||
} else {
|
||||
ctx.fireChannelRead(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-8
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.bukkit.fallback;
|
||||
package xyz.jonesdev.sonar.bukkit.antibot;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
@@ -26,7 +26,7 @@ import lombok.experimental.UtilityClass;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.common.fallback.netty.FallbackInjectedChannelInitializer;
|
||||
import xyz.jonesdev.sonar.common.netty.SonarInjectedChannelInitializer;
|
||||
import xyz.jonesdev.sonar.common.util.exception.ReflectiveOperationException;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
@@ -34,8 +34,8 @@ import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.FallbackPipelines.FALLBACK_INACTIVE_LISTENER;
|
||||
import static xyz.jonesdev.sonar.api.fallback.FallbackPipelines.FALLBACK_PACKET_DECODER;
|
||||
import static xyz.jonesdev.sonar.api.antibot.ChannelPipelines.SONAR_INACTIVE_LISTENER;
|
||||
import static xyz.jonesdev.sonar.api.antibot.ChannelPipelines.SONAR_PACKET_DECODER;
|
||||
import static xyz.jonesdev.sonar.bukkit.SonarBukkit.INITIALIZE_LISTENER;
|
||||
|
||||
// Check out these links if you want to see some more magic
|
||||
@@ -43,7 +43,7 @@ import static xyz.jonesdev.sonar.bukkit.SonarBukkit.INITIALIZE_LISTENER;
|
||||
// https://github.com/dmulloy2/ProtocolLib/blob/master/TinyProtocol/src/main/java/com/comphenix/tinyprotocol/TinyProtocol.java
|
||||
// https://github.com/ViaVersion/ViaVersion/blob/master/bukkit/src/main/java/com/viaversion/viaversion/bukkit/handlers/BukkitChannelInitializer.java
|
||||
@UtilityClass
|
||||
public class FallbackBukkitInjector {
|
||||
public class BukkitInjector {
|
||||
private final String LEGACY_NMS_PACKAGE;
|
||||
private final String OBC_PACKAGE;
|
||||
|
||||
@@ -253,10 +253,10 @@ public class FallbackBukkitInjector {
|
||||
|
||||
INITIALIZE_LISTENER.thenAccept(__ -> {
|
||||
try {
|
||||
childHandlerField.set(_bootstrap, new FallbackInjectedChannelInitializer(originalInitializer,
|
||||
childHandlerField.set(_bootstrap, new SonarInjectedChannelInitializer(originalInitializer,
|
||||
pipeline -> {
|
||||
pipeline.addAfter("splitter", FALLBACK_PACKET_DECODER, new FallbackBukkitInboundHandler());
|
||||
pipeline.addFirst(FALLBACK_INACTIVE_LISTENER, new ChannelInactiveListener());
|
||||
pipeline.addAfter("splitter", SONAR_PACKET_DECODER, new BukkitInboundHandler());
|
||||
pipeline.addFirst(SONAR_INACTIVE_LISTENER, new ChannelInactiveListener());
|
||||
}
|
||||
));
|
||||
} catch (IllegalAccessException exception) {
|
||||
+9
-2
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.bukkit.fallback;
|
||||
package xyz.jonesdev.sonar.bukkit.antibot;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@@ -74,7 +74,14 @@ public enum BukkitServerVersion {
|
||||
MINECRAFT_1_21_1,
|
||||
MINECRAFT_1_21_2,
|
||||
MINECRAFT_1_21_3,
|
||||
MINECRAFT_1_21_4;
|
||||
MINECRAFT_1_21_4,
|
||||
MINECRAFT_1_21_5,
|
||||
MINECRAFT_1_21_6,
|
||||
MINECRAFT_1_21_7,
|
||||
MINECRAFT_1_21_8,
|
||||
MINECRAFT_1_21_9,
|
||||
MINECRAFT_1_21_10,
|
||||
MINECRAFT_1_21_11;
|
||||
|
||||
static final BukkitServerVersion[] REVERSED_VALUES;
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.bukkit.fallback;
|
||||
package xyz.jonesdev.sonar.bukkit.antibot;
|
||||
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.*;
|
||||
@@ -24,7 +24,7 @@ import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.bukkit.fallback.FallbackBukkitInjector;
|
||||
import xyz.jonesdev.sonar.bukkit.antibot.BukkitInjector;
|
||||
import xyz.jonesdev.sonar.common.util.FakeChannelUtil;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
@@ -32,7 +32,7 @@ import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.FallbackPipelines.FALLBACK_BANDWIDTH;
|
||||
import static xyz.jonesdev.sonar.api.antibot.ChannelPipelines.SONAR_BANDWIDTH;
|
||||
|
||||
// https://github.com/ViaVersion/ViaVersion/blob/master/bukkit/src/main/java/com/viaversion/viaversion/bukkit/listeners/JoinListener.java
|
||||
public final class BukkitJoinListener implements Listener {
|
||||
@@ -46,13 +46,13 @@ public final class BukkitJoinListener implements Listener {
|
||||
|
||||
static {
|
||||
try {
|
||||
final Method handleMethod = FallbackBukkitInjector.getOBCClass("entity.CraftPlayer")
|
||||
final Method handleMethod = BukkitInjector.getOBCClass("entity.CraftPlayer")
|
||||
.getDeclaredMethod("getHandle");
|
||||
final Field listenerField = FallbackBukkitInjector.findField(false, handleMethod.getReturnType(),
|
||||
final Field listenerField = BukkitInjector.findField(false, handleMethod.getReturnType(),
|
||||
"PlayerConnection", "ServerGamePacketListenerImpl");
|
||||
final Field networkManagerField = FallbackBukkitInjector.findField(true, listenerField.getType(),
|
||||
final Field networkManagerField = BukkitInjector.findField(true, listenerField.getType(),
|
||||
"NetworkManager", "Connection");
|
||||
final Field channelField = FallbackBukkitInjector.getFieldAt(networkManagerField.getType(), Channel.class, 0);
|
||||
final Field channelField = BukkitInjector.getFieldAt(networkManagerField.getType(), Channel.class, 0);
|
||||
|
||||
_handleMethod = MethodHandles.lookup().unreflect(handleMethod);
|
||||
_listenerField = MethodHandles.lookup().unreflectGetter(listenerField);
|
||||
@@ -88,7 +88,7 @@ public final class BukkitJoinListener implements Listener {
|
||||
}
|
||||
|
||||
// Close the channel if Sonar's handlers are not found when the PlayerJoinEvent is called
|
||||
if (channel.pipeline().context(FALLBACK_BANDWIDTH) == null) {
|
||||
if (channel.pipeline().context(SONAR_BANDWIDTH) == null) {
|
||||
channel.close();
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
|
||||
@@ -28,8 +28,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import xyz.jonesdev.sonar.api.SonarPlatform;
|
||||
import xyz.jonesdev.sonar.api.logger.LoggerWrapper;
|
||||
import xyz.jonesdev.sonar.bungee.antibot.BungeeInjector;
|
||||
import xyz.jonesdev.sonar.bungee.command.BungeeSonarCommand;
|
||||
import xyz.jonesdev.sonar.bungee.fallback.FallbackBungeeInjector;
|
||||
import xyz.jonesdev.sonar.common.boot.SonarBootstrap;
|
||||
|
||||
import java.util.UUID;
|
||||
@@ -86,6 +86,10 @@ public final class SonarBungee extends SonarBootstrap<SonarBungeePlugin> {
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
// Add warning because of questionable design choices that haunt me in my dreams.
|
||||
logger.warn("The use of BungeeCord is supported but not condoned.");
|
||||
logger.warn("Consider upgrading to Velocity (https://velocitypowered.com)");
|
||||
|
||||
// Initialize bStats.org metrics
|
||||
metrics = new Metrics(getPlugin(), getPlatform().getMetricsId());
|
||||
|
||||
@@ -103,7 +107,7 @@ public final class SonarBungee extends SonarBootstrap<SonarBungeePlugin> {
|
||||
getPlugin().getServer().getPluginManager().registerCommand(getPlugin(), new BungeeSonarCommand());
|
||||
|
||||
// Make sure to inject into the server's connection handler
|
||||
FallbackBungeeInjector.inject();
|
||||
BungeeInjector.inject();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-4
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.bungee.fallback;
|
||||
package xyz.jonesdev.sonar.bungee.antibot;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
@@ -25,16 +25,16 @@ import net.md_5.bungee.protocol.PacketWrapper;
|
||||
import net.md_5.bungee.protocol.packet.Handshake;
|
||||
import net.md_5.bungee.protocol.packet.LoginRequest;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.common.fallback.FallbackInboundHandlerAdapter;
|
||||
import xyz.jonesdev.sonar.common.InboundHandlerAdapter;
|
||||
import xyz.jonesdev.sonar.common.util.exception.ReflectiveOperationException;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.packets.handshake.HandshakePacket.STATUS;
|
||||
import static xyz.jonesdev.sonar.common.protocol.packets.handshake.HandshakePacket.STATUS;
|
||||
|
||||
final class FallbackBungeeInboundHandler extends FallbackInboundHandlerAdapter {
|
||||
final class BungeeInboundHandler extends InboundHandlerAdapter {
|
||||
|
||||
private static final MethodHandle CHANNEL_WRAPPER_GETTER;
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.bungee.antibot;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import lombok.val;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.netty.PipelineUtils;
|
||||
import net.md_5.bungee.protocol.channel.BungeeChannelInitializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import sun.misc.Unsafe;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.common.netty.SonarInjectedChannelInitializer;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import static net.md_5.bungee.netty.PipelineUtils.PACKET_DECODER;
|
||||
import static xyz.jonesdev.sonar.api.antibot.ChannelPipelines.SONAR_PACKET_HANDLER;
|
||||
|
||||
@UtilityClass
|
||||
public class BungeeInjector {
|
||||
public void inject() {
|
||||
try {
|
||||
final Field childField;
|
||||
|
||||
try {
|
||||
// This field only exists on BungeeCord build 1908 and below.
|
||||
// See https://github.com/SpigotMC/BungeeCord/pull/3776 for more info.
|
||||
//noinspection all
|
||||
childField = PipelineUtils.class.getField("SERVER_CHILD");
|
||||
} catch (NoSuchFieldException exception) {
|
||||
injectOnModernBungeeCord();
|
||||
return;
|
||||
}
|
||||
|
||||
childField.setAccessible(true);
|
||||
injectOnOldBungeeCord(childField);
|
||||
} catch (Exception exception) {
|
||||
Sonar.get0().getLogger().error("An error occurred while injecting {}", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static void injectOnOldBungeeCord(final @NotNull Field childField) throws Exception {
|
||||
// Make sure to store the original channel initializer
|
||||
//noinspection unchecked
|
||||
val originalInitializer = (ChannelInitializer<Channel>) childField.get(null);
|
||||
final ChannelInitializer<Channel> injectedInitializer = new SonarInjectedChannelInitializer(
|
||||
originalInitializer, pipeline -> pipeline.addAfter(PACKET_DECODER, SONAR_PACKET_HANDLER,
|
||||
new BungeeInboundHandler()));
|
||||
|
||||
final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
|
||||
unsafeField.setAccessible(true);
|
||||
final Unsafe unsafe = (Unsafe) unsafeField.get(null);
|
||||
|
||||
final Object base = unsafe.staticFieldBase(childField);
|
||||
final long offset = unsafe.staticFieldOffset(childField);
|
||||
|
||||
// Replace the original channel initializer with our new injected initializer
|
||||
unsafe.putObject(base, offset, injectedInitializer);
|
||||
}
|
||||
|
||||
// See https://github.com/SpigotMC/BungeeCord/pull/3787
|
||||
private static void injectOnModernBungeeCord() {
|
||||
// The order is important...
|
||||
final BungeeChannelInitializer original = ProxyServer.getInstance().unsafe().getFrontendChannelInitializer();
|
||||
final BungeeChannelInitializer newInitializer = BungeeChannelInitializer.create(
|
||||
channel -> {
|
||||
// https://github.com/SpigotMC/BungeeCord/pull/3787#issuecomment-2661059876
|
||||
if (!original.getChannelAcceptor().accept(channel)) {
|
||||
return false;
|
||||
}
|
||||
SonarInjectedChannelInitializer.inject(channel,
|
||||
pipeline -> pipeline.addAfter(PACKET_DECODER, SONAR_PACKET_HANDLER,
|
||||
new BungeeInboundHandler()));
|
||||
return true;
|
||||
});
|
||||
ProxyServer.getInstance().unsafe().setFrontendChannelInitializer(newInitializer);
|
||||
}
|
||||
}
|
||||
-59
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.bungee.fallback;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import lombok.experimental.UtilityClass;
|
||||
import net.md_5.bungee.netty.PipelineUtils;
|
||||
import sun.misc.Unsafe;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.common.fallback.netty.FallbackInjectedChannelInitializer;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import static net.md_5.bungee.netty.PipelineUtils.PACKET_DECODER;
|
||||
import static xyz.jonesdev.sonar.api.fallback.FallbackPipelines.FALLBACK_PACKET_HANDLER;
|
||||
|
||||
@UtilityClass
|
||||
public class FallbackBungeeInjector {
|
||||
public void inject() {
|
||||
try {
|
||||
final Field childField = PipelineUtils.class.getField("SERVER_CHILD");
|
||||
childField.setAccessible(true);
|
||||
|
||||
// Make sure to store the original channel initializer
|
||||
final ChannelInitializer<Channel> originalInitializer = PipelineUtils.SERVER_CHILD;
|
||||
final ChannelInitializer<Channel> injectedInitializer = new FallbackInjectedChannelInitializer(
|
||||
originalInitializer, pipeline -> pipeline.addAfter(PACKET_DECODER, FALLBACK_PACKET_HANDLER,
|
||||
new FallbackBungeeInboundHandler()));
|
||||
|
||||
final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
|
||||
unsafeField.setAccessible(true);
|
||||
final Unsafe unsafe = (Unsafe) unsafeField.get(null);
|
||||
|
||||
final Object base = unsafe.staticFieldBase(childField);
|
||||
final long offset = unsafe.staticFieldOffset(childField);
|
||||
|
||||
// Replace the original channel initializer with our new injected initializer
|
||||
unsafe.putObject(base, offset, injectedInitializer);
|
||||
} catch (Exception exception) {
|
||||
Sonar.get0().getLogger().error("An error occurred while injecting {}", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,6 @@ dependencies {
|
||||
compileOnly(project(":api"))
|
||||
compileOnly(rootProject.libs.imagefilters)
|
||||
|
||||
testCompileOnly(project(":api"))
|
||||
testCompileOnly(rootProject.libs.imagefilters)
|
||||
testImplementation(project(":api"))
|
||||
testImplementation(rootProject.libs.imagefilters)
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import xyz.jonesdev.sonar.api.fallback.captcha.CaptchaGenerator;
|
||||
import xyz.jonesdev.sonar.api.antibot.captcha.CaptchaGenerator;
|
||||
import xyz.jonesdev.sonar.captcha.filters.CurvesOverlayFilter;
|
||||
import xyz.jonesdev.sonar.captcha.filters.NoiseOverlayFilter;
|
||||
|
||||
@@ -49,8 +49,11 @@ public final class StandardCaptchaGenerator implements CaptchaGenerator {
|
||||
private static final Random RANDOM = new Random();
|
||||
private static final Color[] COLORS = new Color[4];
|
||||
private static final float[] COLOR_FRACTIONS = new float[COLORS.length];
|
||||
static final Font FONT;
|
||||
|
||||
static {
|
||||
FONT = TTFFontProvider.loadFont("/assets/fonts/Kingthings_Trypewriter_2.ttf");
|
||||
|
||||
FBM.setAmount(0.6f);
|
||||
FBM.setScale(15);
|
||||
|
||||
@@ -109,11 +112,11 @@ public final class StandardCaptchaGenerator implements CaptchaGenerator {
|
||||
// Randomize the colors for the gradient effect
|
||||
for (int i = 0; i < COLORS.length; i++) {
|
||||
final float random = 0.9f + RANDOM.nextFloat() * 0.1f;
|
||||
COLORS[i] = Color.getHSBColor(RANDOM.nextFloat(), random, random);
|
||||
COLORS[i] = Color.getHSBColor(RANDOM.nextFloat(), random * 0.8f, random);
|
||||
}
|
||||
|
||||
// Apply the random gradient effect
|
||||
graphics.setPaint(new LinearGradientPaint(0, 0, width, height,
|
||||
graphics.setPaint(new RadialGradientPaint(0, 0, width,
|
||||
COLOR_FRACTIONS, COLORS, MultipleGradientPaint.CycleMethod.REFLECT));
|
||||
}
|
||||
|
||||
@@ -124,8 +127,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 = StandardTTFFontProvider.FONTS[RANDOM.nextInt(StandardTTFFontProvider.FONTS.length)];
|
||||
glyphs[i] = font.createGlyphVector(ctx, String.valueOf(answer[i]));
|
||||
glyphs[i] = FONT.createGlyphVector(ctx, String.valueOf(answer[i]));
|
||||
}
|
||||
|
||||
final double scalingXY = 5 - Math.min(answer.length, 5) * 0.65;
|
||||
@@ -135,7 +137,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 + StandardTTFFontProvider.STANDARD_FONT_SIZE / 2D) / 2D + scalingXY;
|
||||
double beginY = (height + TTFFontProvider.STANDARD_FONT_SIZE / 2D) / 2D + scalingXY;
|
||||
|
||||
// Draw each glyph one by one
|
||||
for (final GlyphVector glyph : glyphs) {
|
||||
|
||||
+3
-13
@@ -17,33 +17,23 @@
|
||||
|
||||
package xyz.jonesdev.sonar.captcha;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.InputStream;
|
||||
import java.util.Objects;
|
||||
|
||||
@UtilityClass
|
||||
class StandardTTFFontProvider {
|
||||
private static final String[] FONT_NAMES = {"Kingthings_Trypewriter_2"};
|
||||
static final Font[] FONTS = new Font[FONT_NAMES.length];
|
||||
public final class TTFFontProvider {
|
||||
static final int STANDARD_FONT_SIZE = 25;
|
||||
|
||||
static {
|
||||
for (int i = 0; i < FONT_NAMES.length; i++) {
|
||||
FONTS[i] = loadFont(String.format("/assets/fonts/%s.ttf", FONT_NAMES[i]));
|
||||
}
|
||||
}
|
||||
|
||||
private static Font loadFont(final @NotNull String path) {
|
||||
public static Font loadFont(final @NotNull String path) {
|
||||
try (final InputStream inputStream = StandardCaptchaGenerator.class.getResourceAsStream(path)) {
|
||||
// Load the font from the TTF file
|
||||
final Font customFont = Font.createFont(Font.TRUETYPE_FONT, Objects.requireNonNull(inputStream));
|
||||
// Set the font size and style
|
||||
return customFont.deriveFont(Font.PLAIN, STANDARD_FONT_SIZE);
|
||||
} catch (Exception exception) {
|
||||
throw new IllegalStateException(exception);
|
||||
throw new IllegalStateException("Error loading font. Does your environment support fonts?", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.captcha.complex;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.antibot.captcha.CaptchaGenerator;
|
||||
import xyz.jonesdev.sonar.captcha.TTFFontProvider;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.font.TextLayout;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.QuadCurve2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Random;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class ComplexCaptchaGenerator implements CaptchaGenerator {
|
||||
private final File background;
|
||||
private final int width = 128, height = 128;
|
||||
private BufferedImage backgroundImage;
|
||||
|
||||
private static final Font[] FONTS = {
|
||||
TTFFontProvider.loadFont("/assets/fonts/dotness.ttf"), // by http://bythebutterfly.com/
|
||||
TTFFontProvider.loadFont("/assets/fonts/DotMatrix.ttf"),
|
||||
};
|
||||
private static final Random RANDOM = new SecureRandom();
|
||||
private static final double MAX_ROTATION_RADIANS = Math.toRadians(15);
|
||||
private static final int LINE_COUNT = 8;
|
||||
private static final int MIN_LINE_COLOR = 100;
|
||||
private static final int MAX_TEXT_COLOR = 150;
|
||||
private static final int DOT_COUNT = 100;
|
||||
|
||||
private @NotNull BufferedImage createBackgroundImage() {
|
||||
final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
|
||||
final Graphics2D graphics = image.createGraphics();
|
||||
if (background == null) {
|
||||
graphics.setBackground(Color.WHITE);
|
||||
graphics.clearRect(0, 0, image.getWidth(), image.getHeight());
|
||||
return image;
|
||||
}
|
||||
if (backgroundImage == null) {
|
||||
try {
|
||||
backgroundImage = ImageIO.read(background);
|
||||
} catch (IOException exception) {
|
||||
throw new IllegalStateException("Could not read background image", exception);
|
||||
}
|
||||
}
|
||||
graphics.drawImage(backgroundImage, 0, 0, Color.WHITE, null);
|
||||
return image;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull BufferedImage createImage(char @NotNull [] answer) {
|
||||
final BufferedImage image = createBackgroundImage();
|
||||
final Graphics2D graphics = image.createGraphics();
|
||||
|
||||
final String text = new String(answer);
|
||||
final int padding = 12;
|
||||
final double maxWidth = width - 2D * padding;
|
||||
final double maxHeight = height - 2D * padding;
|
||||
|
||||
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
|
||||
final FontRenderContext frc = graphics.getFontRenderContext();
|
||||
float optimalSize = 24;
|
||||
final float trialSize = 100;
|
||||
|
||||
final Rectangle2D trialBounds = FONTS[0].deriveFont(trialSize).getStringBounds(text, frc);
|
||||
|
||||
if (trialBounds.getWidth() > 0 && trialBounds.getHeight() > 0) {
|
||||
final double scaleFactorWidth = maxWidth / trialBounds.getWidth();
|
||||
final double scaleFactorHeight = maxHeight / trialBounds.getHeight();
|
||||
optimalSize = (float) (trialSize * Math.min(scaleFactorWidth, scaleFactorHeight));
|
||||
}
|
||||
|
||||
if (optimalSize < 1f) {
|
||||
optimalSize = 1f;
|
||||
}
|
||||
|
||||
final Font finalFont = FONTS[0].deriveFont(optimalSize);
|
||||
graphics.setFont(finalFont);
|
||||
|
||||
final Rectangle2D fullBounds = finalFont.getStringBounds(text, frc);
|
||||
|
||||
double currentX = (width - fullBounds.getWidth()) / 2D;
|
||||
|
||||
final double baselineY = (height / 2D) - (fullBounds.getHeight() / 2D) - fullBounds.getY();
|
||||
double currentY = baselineY;
|
||||
|
||||
for (final char c : answer) {
|
||||
final Font font = FONTS[RANDOM.nextInt(FONTS.length)].deriveFont(optimalSize);
|
||||
graphics.setFont(font);
|
||||
|
||||
final String character = String.valueOf(c);
|
||||
final Rectangle2D charBounds = font.getStringBounds(character, frc);
|
||||
|
||||
final double charWidth = charBounds.getWidth();
|
||||
final double charHeight = charBounds.getHeight();
|
||||
final double charCenterX = currentX + charWidth / 2D;
|
||||
final double charCenterY = baselineY + charBounds.getY() + charHeight / 2D;
|
||||
|
||||
final Color[] colors = new Color[2 + RANDOM.nextInt(2)];
|
||||
final float[] colorFractions = new float[colors.length];
|
||||
for (int i = 0; i < colors.length; i++) {
|
||||
colors[i] = getRandomTextColor();
|
||||
colorFractions[i] = i / (float) colors.length;
|
||||
}
|
||||
|
||||
graphics.setPaint(new RadialGradientPaint((float) currentX, (float) currentY, (float) charWidth,
|
||||
colorFractions, colors, MultipleGradientPaint.CycleMethod.REFLECT));
|
||||
|
||||
final double rotation = (RANDOM.nextDouble() * 2 * MAX_ROTATION_RADIANS) - MAX_ROTATION_RADIANS;
|
||||
|
||||
final AffineTransform originalTransform = graphics.getTransform();
|
||||
|
||||
graphics.rotate(rotation, charCenterX, charCenterY);
|
||||
|
||||
final TextLayout layout = new TextLayout(character, font, frc);
|
||||
|
||||
final AffineTransform translate = AffineTransform.getTranslateInstance(currentX, currentY);
|
||||
final Shape outline = layout.getOutline(translate);
|
||||
graphics.fill(outline);
|
||||
graphics.setTransform(originalTransform);
|
||||
|
||||
currentX += charWidth;
|
||||
currentY += RANDOM.nextDouble(charHeight / 4) - charHeight / 8;
|
||||
}
|
||||
|
||||
drawNoiseLines(graphics);
|
||||
drawNoiseDots(graphics);
|
||||
graphics.dispose();
|
||||
return image;
|
||||
}
|
||||
|
||||
private @NotNull Color getRandomTextColor() {
|
||||
final int r = Math.min(RANDOM.nextInt(256), MAX_TEXT_COLOR);
|
||||
final int g = Math.min(RANDOM.nextInt(256), MAX_TEXT_COLOR);
|
||||
final int b = Math.min(RANDOM.nextInt(256), MAX_TEXT_COLOR);
|
||||
return new Color(r, g, b, 220);
|
||||
}
|
||||
|
||||
private @NotNull Color getRandomColor() {
|
||||
final int r = Math.max(RANDOM.nextInt(256), MIN_LINE_COLOR);
|
||||
final int g = Math.max(RANDOM.nextInt(256), MIN_LINE_COLOR);
|
||||
final int b = Math.max(RANDOM.nextInt(256), MIN_LINE_COLOR);
|
||||
final int a = Math.max(Math.min(RANDOM.nextInt(256), 200), MIN_LINE_COLOR);
|
||||
return new Color(r, g, b, a);
|
||||
}
|
||||
|
||||
private void drawNoiseLines(final @NotNull Graphics2D graphics) {
|
||||
for (int i = 0; i < LINE_COUNT; i++) {
|
||||
graphics.setColor(getRandomColor());
|
||||
graphics.setStroke(new BasicStroke(1f + RANDOM.nextFloat() * 2f,
|
||||
BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
|
||||
|
||||
if (RANDOM.nextBoolean()) {
|
||||
float x1 = RANDOM.nextInt(width);
|
||||
float y1 = RANDOM.nextInt(height);
|
||||
float ctrlX = RANDOM.nextInt(width);
|
||||
float ctrlY = RANDOM.nextInt(height);
|
||||
float x2 = RANDOM.nextInt(width);
|
||||
float y2 = RANDOM.nextInt(height);
|
||||
|
||||
graphics.draw(new QuadCurve2D.Float(x1, y1, ctrlX, ctrlY, x2, y2));
|
||||
} else {
|
||||
int x1 = RANDOM.nextInt(width);
|
||||
int y1 = RANDOM.nextInt(height);
|
||||
int x2 = RANDOM.nextInt(width);
|
||||
int y2 = RANDOM.nextInt(height);
|
||||
|
||||
graphics.drawLine(x1, y1, x2, y2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void drawNoiseDots(final @NotNull Graphics2D graphics) {
|
||||
for (int i = 0; i < DOT_COUNT; i++) {
|
||||
graphics.setColor(getRandomColor());
|
||||
|
||||
final int x = RANDOM.nextInt(width);
|
||||
final int y = RANDOM.nextInt(height);
|
||||
final int radius = 1 + RANDOM.nextInt(5);
|
||||
|
||||
graphics.fillOval(x, y, radius, radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.captcha.filters;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.Random;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public final class CircleInverseFilter {
|
||||
private final int amount, minRadius, maxRadiusExpansion;
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
public void transform(final @NotNull BufferedImage image) {
|
||||
final int minX = image.getWidth() / 4;
|
||||
final int minY = image.getHeight() / 4;
|
||||
|
||||
for (int i = 0; i < amount; i++) {
|
||||
// Generate random position and radius for the circle
|
||||
final float centerX = minX + RANDOM.nextInt(image.getWidth() - minX * 2);
|
||||
final float centerY = minY + RANDOM.nextInt(image.getHeight() - minY * 2);
|
||||
final int radius = minRadius + RANDOM.nextInt(maxRadiusExpansion);
|
||||
|
||||
final Circle circle = new Circle(centerX, centerY, radius);
|
||||
// Draw inverted circle
|
||||
_addInvertedCircle(image, circle);
|
||||
}
|
||||
}
|
||||
|
||||
private static void _addInvertedCircle(final @NotNull BufferedImage bufferedImage,
|
||||
final @NotNull Circle circle) {
|
||||
for (int x = 0; x < bufferedImage.getWidth(); x++) {
|
||||
for (int y = 0; y < bufferedImage.getHeight(); y++) {
|
||||
if (isWithinCircle(x, y, circle, circle.radius)) {
|
||||
// Invert colors within the circle
|
||||
final int rgb = bufferedImage.getRGB(x, y);
|
||||
final int invertedRGB = invertColorAndFilter(rgb);
|
||||
bufferedImage.setRGB(x, y, invertedRGB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isWithinCircle(final float x, final float y, final @NotNull Circle circle, int radius) {
|
||||
final float distanceX = x - circle.centerX;
|
||||
final float distanceY = y - circle.centerY;
|
||||
return distanceX * distanceX + distanceY * distanceY <= radius * radius;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
static final class Circle {
|
||||
private final float centerX;
|
||||
private final float centerY;
|
||||
private final int radius;
|
||||
}
|
||||
|
||||
private static int invertColorAndFilter(final int rgb) {
|
||||
final Color color = new Color(rgb);
|
||||
final int red = Math.max(Color.LIGHT_GRAY.getRed() - color.getRed(), 0);
|
||||
final int green = Math.max(Color.LIGHT_GRAY.getGreen() - color.getGreen(), 0);
|
||||
final int blue = Math.max(Color.LIGHT_GRAY.getBlue() - color.getBlue(), 0);
|
||||
return new Color(red, green, blue).getRGB();
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.captcha.filters;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.Random;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public final class GridOverlayFilter {
|
||||
private final int amount, lineWidth, randomOffset;
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
public void transform(final @NotNull BufferedImage image,
|
||||
final @NotNull Graphics2D graphics) {
|
||||
final int spacingX = image.getWidth() / amount + lineWidth;
|
||||
final int spacingY = image.getHeight() / amount + lineWidth;
|
||||
int currentX = spacingX / 2;
|
||||
int currentY = spacingY / 2;
|
||||
|
||||
for (int i = 0; i < amount; i++) {
|
||||
graphics.setStroke(new BasicStroke(lineWidth * 0.5f + lineWidth * RANDOM.nextFloat()));
|
||||
graphics.drawLine(currentX, 0, currentX + RANDOM.nextInt(randomOffset), image.getHeight());
|
||||
graphics.drawLine(0, currentY, image.getWidth(), currentY + RANDOM.nextInt(randomOffset));
|
||||
|
||||
currentX += spacingX + lineWidth;
|
||||
currentY += spacingY + lineWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.captcha.legacy;
|
||||
|
||||
import com.jhlabs.image.*;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import xyz.jonesdev.sonar.api.antibot.captcha.CaptchaGenerator;
|
||||
import xyz.jonesdev.sonar.captcha.filters.NoiseOverlayFilter;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.font.GlyphVector;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public final class LegacyCaptchaGenerator implements CaptchaGenerator {
|
||||
private static final ScratchFilter SCRATCHES = new ScratchFilter(4);
|
||||
private static final NoiseOverlayFilter NOISE = new NoiseOverlayFilter(1, 7);
|
||||
private static final SmearFilter SMEAR = new SmearFilter();
|
||||
private static final SaturationFilter SATURATION = new SaturationFilter();
|
||||
private static final RippleFilter RIPPLE = new RippleFilter();
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
static {
|
||||
RIPPLE.setXAmplitude(0);
|
||||
float yAmplitude = 10 - RANDOM.nextInt(20);
|
||||
if (Math.abs(yAmplitude) < 5) {
|
||||
yAmplitude = yAmplitude >= 0 ? 5 : -5;
|
||||
}
|
||||
RIPPLE.setYAmplitude(yAmplitude);
|
||||
|
||||
SATURATION.setAmount(0.45f + RANDOM.nextFloat() * 0.15f);
|
||||
|
||||
SMEAR.setShape(SmearFilter.CIRCLES);
|
||||
SMEAR.setMix(0.15f);
|
||||
SMEAR.setDensity(0.1f);
|
||||
SMEAR.setDistance(5);
|
||||
}
|
||||
|
||||
private final int width = 128, height = 128;
|
||||
private final @Nullable File background;
|
||||
private @Nullable BufferedImage backgroundImage;
|
||||
private GradientPaint gradient;
|
||||
|
||||
public BufferedImage createCachedBackgroundImage() {
|
||||
// Create a new image buffer with a 3-byte color spectrum
|
||||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
|
||||
if (background == null) {
|
||||
if (backgroundImage != null) {
|
||||
return backgroundImage;
|
||||
}
|
||||
// Don't use any special background image
|
||||
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
// Fill the entire background image with a noise texture
|
||||
image = new CausticsFilter().filter(image, null);
|
||||
// Adjust the saturation of the randomly generated background noise
|
||||
final SaturationFilter saturationFilter = new SaturationFilter();
|
||||
saturationFilter.setAmount(0.4f + RANDOM.nextFloat() * 0.2f);
|
||||
image = saturationFilter.filter(image, null);
|
||||
// Un-sharpen the background a bit
|
||||
image = new UnsharpFilter().filter(image, null);
|
||||
return image;
|
||||
}
|
||||
// Try to load the image from a file if it doesn't exist yet
|
||||
if (backgroundImage == null) {
|
||||
try {
|
||||
backgroundImage = ImageIO.read(background);
|
||||
} catch (IOException exception) {
|
||||
throw new IllegalStateException("Could not read background image", exception);
|
||||
}
|
||||
}
|
||||
image.createGraphics().drawImage(backgroundImage, 0, 0, Color.WHITE, null);
|
||||
return image;
|
||||
}
|
||||
|
||||
private @NotNull BufferedImage mergeImages(final @NotNull BufferedImage background,
|
||||
final @NotNull BufferedImage foreground) {
|
||||
// Get the background image and create a new foreground image
|
||||
final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
|
||||
// Create a new image with transparency for the merged result
|
||||
final Graphics2D graphics = image.createGraphics();
|
||||
// Draw the foreground image on top of the background at specified coordinates
|
||||
graphics.drawImage(background, 0, 0, null);
|
||||
// Set AlphaComposite to handle transparency for the foreground image
|
||||
graphics.setComposite(AlphaComposite.SrcOver);
|
||||
// Draw the foreground image on top of the background at specified coordinates
|
||||
graphics.drawImage(foreground, 0, 0, null);
|
||||
graphics.dispose();
|
||||
return image;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull BufferedImage createImage(final char @NotNull [] answer) {
|
||||
final Color color0 = Color.getHSBColor(RANDOM.nextFloat(), 1, 1);
|
||||
final Color color1 = Color.getHSBColor(RANDOM.nextFloat(), 1, 0.5f);
|
||||
gradient = new GradientPaint(0, 0, color0, width, height, color1);
|
||||
|
||||
// Get the background image and create a new foreground image
|
||||
BufferedImage foreground = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||
final Graphics2D graphics = foreground.createGraphics();
|
||||
|
||||
final FontRenderContext ctx = graphics.getFontRenderContext();
|
||||
// Change some rendering hints for anti aliasing
|
||||
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
// Draw characters
|
||||
drawCharacters(graphics, ctx, answer);
|
||||
// Make sure to dispose the graphics after using it
|
||||
graphics.dispose();
|
||||
|
||||
// Apply any given filter to the foreground
|
||||
new BumpFilter().filter(foreground, foreground);
|
||||
SMEAR.filter(foreground, foreground);
|
||||
RIPPLE.filter(foreground, foreground);
|
||||
SCRATCHES.filter(foreground, foreground);
|
||||
|
||||
final BufferedImage merged = mergeImages(createCachedBackgroundImage(), foreground);
|
||||
NOISE.transform(merged);
|
||||
return merged;
|
||||
}
|
||||
|
||||
private void drawCharacters(final @NotNull Graphics2D graphics,
|
||||
final @NotNull FontRenderContext ctx,
|
||||
final char[] answer) {
|
||||
// Create font render context
|
||||
final int fontSize = 62 - answer.length * 2;
|
||||
final Font font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize);
|
||||
|
||||
// Calculate string width
|
||||
final double stringWidth = font.getStringBounds(answer, 0, answer.length, ctx).getWidth();
|
||||
// Calculate character positions
|
||||
int beginX = (int) ((width - stringWidth) / 2D + stringWidth / (answer.length * 2.5D));
|
||||
final int beginY = (int) ((height + fontSize / 2D) / 2D);
|
||||
double rotation = 0;
|
||||
|
||||
if (gradient != null) {
|
||||
graphics.setPaint(gradient);
|
||||
}
|
||||
|
||||
// Draw each character one by one
|
||||
for (final char character : answer) {
|
||||
// Create a glyph vector for the character
|
||||
final GlyphVector glyphVector = font.createGlyphVector(ctx, String.valueOf(character));
|
||||
final int visualBoundsWidth = (int) glyphVector.getVisualBounds().getWidth();
|
||||
if (gradient == null) {
|
||||
Objects.requireNonNull(backgroundImage);
|
||||
// Use the inverse color by checking the background image
|
||||
// Then, create a gradient for the text by using the colors
|
||||
final int x0 = Math.min(Math.max(beginX + 5 /* small threshold */, 0), width);
|
||||
final int x1 = Math.min(Math.max(beginX + (int) visualBoundsWidth, 0), width);
|
||||
final int y = beginY + fontSize / 2;
|
||||
final Color color0 = new Color(~backgroundImage.getRGB(x0, y));
|
||||
final Color color1 = new Color(~backgroundImage.getRGB(x1, y));
|
||||
final GradientPaint gradient = new GradientPaint(0, 0, color0, width, height, color1);
|
||||
graphics.setPaint(gradient);
|
||||
}
|
||||
|
||||
// Apply a transformation to the glyph vector using AffineTransform
|
||||
final AffineTransform transformation = AffineTransform.getTranslateInstance(beginX, beginY);
|
||||
|
||||
// Add a bit of randomization to the rotation
|
||||
rotation += Math.toRadians(6 - RANDOM.nextInt(12));
|
||||
transformation.rotate(rotation);
|
||||
transformation.scale(1, 1.25);
|
||||
|
||||
// Draw the glyph to the buffered image
|
||||
final Shape transformedShape = transformation.createTransformedShape(glyphVector.getOutline());
|
||||
graphics.fill(transformedShape);
|
||||
// Add text outline/shadow to confuse an AI's edge detection
|
||||
addTextOutline(graphics, transformedShape);
|
||||
// Update next X position
|
||||
beginX += visualBoundsWidth + 2;
|
||||
}
|
||||
}
|
||||
|
||||
private static void addTextOutline(final @NotNull Graphics2D graphics,
|
||||
final @NotNull Shape transformedShape) {
|
||||
// Create a stroked copy of the text and slightly offset/distort it
|
||||
final Shape strokedShape = new BasicStroke().createStrokedShape(transformedShape);
|
||||
|
||||
final double tx = 0.5 + RANDOM.nextDouble();
|
||||
final double ty = 0.5 + RANDOM.nextDouble();
|
||||
|
||||
// Draw the stroked shape
|
||||
final AffineTransform transform = AffineTransform.getTranslateInstance(tx, ty);
|
||||
graphics.fill(transform.createTransformedShape(strokedShape));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.captcha.legacy;
|
||||
|
||||
import com.jhlabs.image.TransformFilter;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class RippleFilter extends TransformFilter {
|
||||
private float xAmplitude, yAmplitude;
|
||||
|
||||
@Override
|
||||
protected void transformInverse(final int x, final int y, final float @NotNull [] out) {
|
||||
final float nx = (float) y / 13f;
|
||||
final float ny = (float) x / 13f;
|
||||
final float fx = (float) Math.sin(nx);
|
||||
final float fy = (float) Math.sin(ny);
|
||||
|
||||
out[0] = (float) x + xAmplitude * fx;
|
||||
out[1] = (float) y + yAmplitude * fy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.captcha.legacy;
|
||||
|
||||
import com.jhlabs.image.AbstractBufferedImageOp;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.Random;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class ScratchFilter extends AbstractBufferedImageOp {
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
private final int amount;
|
||||
|
||||
@Override
|
||||
public @NotNull BufferedImage filter(final @NotNull BufferedImage src, final BufferedImage dst) {
|
||||
final Graphics2D graphics = src.createGraphics();
|
||||
|
||||
// Apply some gradient effect on them
|
||||
final Color color0 = Color.getHSBColor(RANDOM.nextFloat(), RANDOM.nextFloat(), 1);
|
||||
final Color color1 = new Color(~color0.getRGB());
|
||||
final GradientPaint gradient = new GradientPaint(0, 0, color0, src.getWidth(), src.getHeight(), color1);
|
||||
graphics.setPaint(gradient);
|
||||
|
||||
final float halfWidth = src.getWidth() * 0.5f;
|
||||
|
||||
for (int i = 0; i < amount; ++i) {
|
||||
final float randomX = src.getWidth() * RANDOM.nextFloat();
|
||||
final float randomY = src.getHeight() * RANDOM.nextFloat();
|
||||
final float amplitude = 6.2831855f * (RANDOM.nextFloat() - 0.5f);
|
||||
final float sin = (float) Math.sin(amplitude) * halfWidth;
|
||||
final float cos = (float) Math.cos(amplitude) * halfWidth;
|
||||
final float x1 = randomX - cos;
|
||||
final float y1 = randomY - sin;
|
||||
final float x2 = randomX + cos;
|
||||
final float y2 = randomY + sin;
|
||||
graphics.drawLine((int) x1, (int) y1, (int) x2, (int) y2);
|
||||
}
|
||||
|
||||
graphics.dispose();
|
||||
return src;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,5 @@
|
||||
My fonts are free for personal use. You also MAY redistribute the fonts on your website as long as you guve credit to http://bythebutterfly.com as the creator of the fonts. If you are interested in commercial use, please email me at BYTHEBUTTERFLY@GMAIL.COM
|
||||
|
||||
Thanks, and enjoy :)
|
||||
|
||||
Vanessa
|
||||
@@ -0,0 +1,12 @@
|
||||
Kingthings EULA - (End User License Agreement).
|
||||
|
||||
All the fonts currently published on my website (www.kingthingsfonts.co.uk) are free for you to use for any purpose you wish. Please do not change my original font files (.TTF).
|
||||
|
||||
There are some paid-for versions of my fonts (with much enhanced character sets) available from to buy from www.cheapprofonts.com, these contain their own EULA and are not included or implied in this notice.
|
||||
|
||||
Please enjoy all your creative work - and if you come up with anything you like, I would appreciate a picture.
|
||||
|
||||
Regards
|
||||
|
||||
Kevin King
|
||||
Exeter, England - February 2009
|
||||
@@ -16,7 +16,8 @@
|
||||
*/
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import xyz.jonesdev.sonar.captcha.StandardCaptchaGenerator;
|
||||
import xyz.jonesdev.sonar.api.antibot.captcha.CaptchaGenerator;
|
||||
import xyz.jonesdev.sonar.captcha.complex.ComplexCaptchaGenerator;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
@@ -28,7 +29,7 @@ import java.util.Random;
|
||||
public class Main {
|
||||
public void main(final String... args) throws IOException {
|
||||
final Random random = new Random();
|
||||
final StandardCaptchaGenerator standardCaptchaGenerator = new StandardCaptchaGenerator(null);
|
||||
final CaptchaGenerator standardCaptchaGenerator = new ComplexCaptchaGenerator(null);
|
||||
|
||||
final long start = System.currentTimeMillis();
|
||||
final char[] dictionary = {'a', 'b', 'c', 'd', 'e', 'f'/*, 'g'*/, 'h'/*, 'i'*/, 'j',
|
||||
|
||||
+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;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelDuplexHandler;
|
||||
@@ -29,8 +29,8 @@ import xyz.jonesdev.sonar.common.statistics.GlobalSonarStatistics;
|
||||
|
||||
@ChannelHandler.Sharable
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class FallbackBandwidthHandler extends ChannelDuplexHandler {
|
||||
public static final ChannelHandler INSTANCE = new FallbackBandwidthHandler();
|
||||
public final class BandwidthHandler extends ChannelDuplexHandler {
|
||||
public static final ChannelHandler INSTANCE = new BandwidthHandler();
|
||||
|
||||
@Override
|
||||
public void channelRead(final ChannelHandlerContext ctx, final @NotNull Object msg) throws Exception {
|
||||
+5
-5
@@ -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;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
@@ -31,7 +31,7 @@ import java.net.InetAddress;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class FallbackInboundHandler extends ChannelInboundHandlerAdapter {
|
||||
public final class InboundHandler extends ChannelInboundHandlerAdapter {
|
||||
private final Consumer<ChannelPipeline> sonarPipelineInjector;
|
||||
@Setter
|
||||
@Getter
|
||||
@@ -55,11 +55,11 @@ public final class FallbackInboundHandler extends ChannelInboundHandlerAdapter {
|
||||
// Account for this by checking if the inetAddress has been set yet
|
||||
if (inetAddress != null) {
|
||||
// Remove the IP address from the queue
|
||||
Sonar.get0().getFallback().getQueue().getPlayers().compute(inetAddress, (ignored, action) -> {
|
||||
Sonar.get0().getAntiBot().getQueue().getPlayers().compute(inetAddress, (ignored, action) -> {
|
||||
// The player is not queued, so we need to remove them from other maps as well
|
||||
if (action == null) {
|
||||
// Remove the IP address from the connected players, if needed
|
||||
Sonar.get0().getFallback().getConnected().compute(inetAddress, (__, v) -> {
|
||||
Sonar.get0().getAntiBot().getConnected().compute(inetAddress, (__, v) -> {
|
||||
/*
|
||||
* Remove this account from the online players or decrement the number of accounts
|
||||
* with the same IP, but only if the player is logging into the backend server.
|
||||
@@ -67,7 +67,7 @@ public final class FallbackInboundHandler extends ChannelInboundHandlerAdapter {
|
||||
* we've never actually incremented it in the first place ¯\_(ツ)_/¯
|
||||
*/
|
||||
if (v == null) {
|
||||
Sonar.get0().getFallback().getOnline().compute(inetAddress,
|
||||
Sonar.get0().getAntiBot().getOnline().compute(inetAddress,
|
||||
(ignored1, count) -> count == null || count <= 1 ? null : count - 1);
|
||||
}
|
||||
return null;
|
||||
+39
-46
@@ -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;
|
||||
|
||||
import io.netty.channel.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -23,12 +23,12 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.api.fingerprint.FingerprintingUtil;
|
||||
import xyz.jonesdev.sonar.common.fallback.netty.FallbackTimeoutHandler;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketEncoder;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketRegistry;
|
||||
import xyz.jonesdev.sonar.common.netty.SonarTimeoutHandler;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacketEncoder;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacketRegistry;
|
||||
import xyz.jonesdev.sonar.common.statistics.GlobalSonarStatistics;
|
||||
import xyz.jonesdev.sonar.common.util.GeyserUtil;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
@@ -39,15 +39,15 @@ import java.net.InetSocketAddress;
|
||||
import java.util.Map;
|
||||
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.api.antibot.ChannelPipelines.*;
|
||||
import static xyz.jonesdev.sonar.common.protocol.SonarPacketPreparer.*;
|
||||
|
||||
@AllArgsConstructor
|
||||
@RequiredArgsConstructor
|
||||
public abstract class FallbackInboundHandlerAdapter extends ChannelInboundHandlerAdapter {
|
||||
public abstract class InboundHandlerAdapter extends ChannelInboundHandlerAdapter {
|
||||
protected @Nullable String username;
|
||||
protected ProtocolVersion protocolVersion;
|
||||
protected RemovalListener channelRemovalListener = RemovalListener.EMPTY;
|
||||
protected @Nullable RemovalListener channelRemovalListener;
|
||||
|
||||
/**
|
||||
* Validates and handles incoming handshake packets
|
||||
@@ -64,7 +64,7 @@ public abstract class FallbackInboundHandlerAdapter extends ChannelInboundHandle
|
||||
throw QuietDecoderException.INSTANCE;
|
||||
}
|
||||
protocolVersion = ProtocolVersion.fromId(protocol);
|
||||
ctx.pipeline().addFirst(FALLBACK_BANDWIDTH, FallbackBandwidthHandler.INSTANCE);
|
||||
ctx.pipeline().addFirst(SONAR_BANDWIDTH, BandwidthHandler.INSTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,10 +94,10 @@ public abstract class FallbackInboundHandlerAdapter extends ChannelInboundHandle
|
||||
this.username = username;
|
||||
|
||||
final InetAddress inetAddress = socketAddress.getAddress();
|
||||
ctx.pipeline().get(FallbackInboundHandler.class).setInetAddress(inetAddress);
|
||||
ctx.pipeline().get(InboundHandler.class).setInetAddress(inetAddress);
|
||||
|
||||
// Check if Fallback is already verifying a player with the same IP address
|
||||
if (Sonar.get0().getFallback().getConnected().containsKey(inetAddress)) {
|
||||
// Check if Sonar is already verifying a player with the same IP address
|
||||
if (Sonar.get0().getAntiBot().getConnected().containsKey(inetAddress)) {
|
||||
customDisconnect(ctx.channel(), alreadyVerifying, protocolVersion);
|
||||
return;
|
||||
}
|
||||
@@ -112,7 +112,7 @@ public abstract class FallbackInboundHandlerAdapter extends ChannelInboundHandle
|
||||
// Check if the player failed the verification too many times
|
||||
final int limit = Sonar.get0().getConfig().getVerification().getBlacklistThreshold();
|
||||
if (limit > 0) {
|
||||
final int score = Sonar.get0().getFallback().getBlacklist().asMap().getOrDefault(hostAddress, 0);
|
||||
final int score = Sonar.get0().getAntiBot().getBlacklist().asMap().getOrDefault(hostAddress, 0);
|
||||
if (score >= limit) {
|
||||
customDisconnect(ctx.channel(), blacklisted, protocolVersion);
|
||||
return;
|
||||
@@ -120,7 +120,7 @@ public abstract class FallbackInboundHandlerAdapter extends ChannelInboundHandle
|
||||
}
|
||||
|
||||
// Don't continue the verification process if the verification is disabled
|
||||
if (!Sonar.get0().getFallback().shouldVerifyNewPlayers()) {
|
||||
if (!Sonar.get0().getAntiBot().shouldVerifyNewPlayers()) {
|
||||
initialLogin(ctx.channel(), inetAddress, initialLoginAction);
|
||||
return;
|
||||
}
|
||||
@@ -140,7 +140,7 @@ public abstract class FallbackInboundHandlerAdapter extends ChannelInboundHandle
|
||||
}
|
||||
|
||||
// Check if the IP address is currently being rate-limited
|
||||
if (!Sonar.get0().getFallback().getRatelimiter().attempt(inetAddress)) {
|
||||
if (!Sonar.get0().getAntiBot().getRatelimiter().attempt(inetAddress)) {
|
||||
customDisconnect(ctx.channel(), reconnectedTooFast, protocolVersion);
|
||||
return;
|
||||
}
|
||||
@@ -149,7 +149,7 @@ public abstract class FallbackInboundHandlerAdapter extends ChannelInboundHandle
|
||||
rewriteProtocol(ctx, channelRemovalListener);
|
||||
|
||||
// Queue the connection for further processing
|
||||
Sonar.get0().getFallback().getQueue().getPlayers().compute(inetAddress, (__, runnable) -> {
|
||||
Sonar.get0().getAntiBot().getQueue().getPlayers().compute(inetAddress, (__, runnable) -> {
|
||||
// Check if the player is already queued since we don't want bots to flood the queue
|
||||
if (runnable != null) {
|
||||
customDisconnect(ctx.channel(), alreadyQueued, protocolVersion);
|
||||
@@ -158,7 +158,7 @@ public abstract class FallbackInboundHandlerAdapter extends ChannelInboundHandle
|
||||
}
|
||||
|
||||
// Create an instance for the user and let the verification handler take over the channel
|
||||
return () -> new FallbackUserWrapper(ctx, inetAddress, protocolVersion, username, fingerprint, geyser);
|
||||
return () -> new UserWrapper(ctx, inetAddress, protocolVersion, username, fingerprint, geyser);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -168,30 +168,23 @@ public abstract class FallbackInboundHandlerAdapter extends ChannelInboundHandle
|
||||
protected final void initialLogin(final @NotNull Channel channel,
|
||||
final @NotNull InetAddress inetAddress,
|
||||
final @NotNull Runnable loginPacket) throws Exception {
|
||||
Sonar.get0().getFallback().getOnline().compute(inetAddress, (__, count) -> {
|
||||
final int maxOnlinePerIp = Sonar.get0().getConfig().getMaxOnlinePerIp();
|
||||
// Skip the maximum online per IP check if it's disabled in the configuration
|
||||
if (count != null && maxOnlinePerIp > 0) {
|
||||
// Check if the number of online players using the same IP address as
|
||||
// the connecting player is greater than the configured amount
|
||||
if (count >= maxOnlinePerIp) {
|
||||
customDisconnect(channel, tooManyOnlinePerIP, protocolVersion);
|
||||
return count + 1;
|
||||
}
|
||||
final int maxOnlinePerIp = Sonar.get0().getConfig().getMaxOnlinePerIp();
|
||||
if (maxOnlinePerIp > 0) {
|
||||
final int newCount = Sonar.get0().getAntiBot().getOnline().compute(inetAddress,
|
||||
(__, count) -> count == null ? 1 : count + 1);
|
||||
if (newCount > maxOnlinePerIp) {
|
||||
customDisconnect(channel, tooManyOnlinePerIP, protocolVersion);
|
||||
return;
|
||||
}
|
||||
|
||||
// Let the server know about the login packet
|
||||
loginPacket.run();
|
||||
// Increment the number of accounts with the same IP
|
||||
return count == null ? 1 : count + 1;
|
||||
});
|
||||
}
|
||||
loginPacket.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all pipelines and rewrites them using our custom handlers
|
||||
*/
|
||||
private static void rewriteProtocol(final @NotNull ChannelHandlerContext ctx,
|
||||
final @NotNull RemovalListener removalListener) {
|
||||
final @Nullable RemovalListener removalListener) {
|
||||
for (final Map.Entry<String, ChannelHandler> entry : ctx.pipeline()) {
|
||||
// Don't accidentally remove Sonar's handlers
|
||||
if (entry.getKey().startsWith("sonar")
|
||||
@@ -202,17 +195,19 @@ public abstract class FallbackInboundHandlerAdapter extends ChannelInboundHandle
|
||||
continue;
|
||||
}
|
||||
ctx.pipeline().remove(entry.getValue());
|
||||
removalListener.accept(ctx.pipeline(), entry.getKey(), entry.getValue());
|
||||
if (removalListener != null) {
|
||||
removalListener.accept(ctx.pipeline(), entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
// Add our read/write timeout handler
|
||||
ctx.pipeline().addFirst(FALLBACK_TIMEOUT, new FallbackTimeoutHandler(
|
||||
ctx.pipeline().addFirst(SONAR_TIMEOUT, new SonarTimeoutHandler(
|
||||
Sonar.get0().getConfig().getVerification().getReadTimeout(),
|
||||
Sonar.get0().getConfig().getVerification().getWriteTimeout(),
|
||||
TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
private static void customDisconnect(final @NotNull Channel channel,
|
||||
final @NotNull FallbackPacket packet,
|
||||
final @NotNull SonarPacket packet,
|
||||
final @NotNull ProtocolVersion protocolVersion) {
|
||||
if (channel.isActive()) {
|
||||
if (channel.eventLoop().inEventLoop()) {
|
||||
@@ -227,7 +222,7 @@ public abstract class FallbackInboundHandlerAdapter extends ChannelInboundHandle
|
||||
* Disconnect the player before verification (during login)
|
||||
*/
|
||||
private static void _customDisconnect(final @NotNull Channel channel,
|
||||
final @NotNull FallbackPacket packet,
|
||||
final @NotNull SonarPacket packet,
|
||||
final @NotNull ProtocolVersion protocolVersion) {
|
||||
// TODO: recode this
|
||||
// Remove the connection handler pipeline to completely take over the channel
|
||||
@@ -240,10 +235,10 @@ public abstract class FallbackInboundHandlerAdapter extends ChannelInboundHandle
|
||||
// Close the channel if no encoder exists
|
||||
if (currentEncoder != null) {
|
||||
// We don't need to update the encoder if it has already been replaced by Sonar
|
||||
if (!(currentEncoder instanceof FallbackPacketEncoder)) {
|
||||
final FallbackPacketEncoder newEncoder = new FallbackPacketEncoder(protocolVersion);
|
||||
newEncoder.updateRegistry(FallbackPacketRegistry.LOGIN);
|
||||
channel.pipeline().replace(encoder, FALLBACK_PACKET_ENCODER, newEncoder);
|
||||
if (!(currentEncoder instanceof SonarPacketEncoder)) {
|
||||
final SonarPacketEncoder newEncoder = new SonarPacketEncoder(protocolVersion);
|
||||
newEncoder.updateRegistry(SonarPacketRegistry.LOGIN);
|
||||
channel.pipeline().replace(encoder, SONAR_PACKET_ENCODER, newEncoder);
|
||||
}
|
||||
ProtocolUtil.closeWith(channel, protocolVersion, packet);
|
||||
} else {
|
||||
@@ -256,7 +251,5 @@ public abstract class FallbackInboundHandlerAdapter extends ChannelInboundHandle
|
||||
void accept(final @NotNull ChannelPipeline pipeline,
|
||||
final @NotNull String name,
|
||||
final @NotNull ChannelHandler handler);
|
||||
|
||||
RemovalListener EMPTY = (pipeline, name, handler) -> {};
|
||||
}
|
||||
}
|
||||
+42
-36
@@ -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;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
@@ -26,29 +26,29 @@ import lombok.experimental.Accessors;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.Sonar;
|
||||
import xyz.jonesdev.sonar.api.antibot.SonarUser;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.api.event.impl.UserVerifyJoinEvent;
|
||||
import xyz.jonesdev.sonar.api.fallback.FallbackUser;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.api.timer.SystemTimer;
|
||||
import xyz.jonesdev.sonar.common.fallback.netty.FallbackTailExceptionsHandler;
|
||||
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.netty.MinecraftVarInt21FrameDecoder;
|
||||
import xyz.jonesdev.sonar.common.netty.MinecraftVarIntLengthEncoder;
|
||||
import xyz.jonesdev.sonar.common.netty.TailExceptionsHandler;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacketDecoder;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacketEncoder;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacketPreparer;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacketRegistry;
|
||||
import xyz.jonesdev.sonar.common.protocol.packets.play.DisconnectPacket;
|
||||
import xyz.jonesdev.sonar.common.statistics.GlobalSonarStatistics;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
import xyz.jonesdev.sonar.common.verification.LoginHandler;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.FallbackPipelines.*;
|
||||
import static xyz.jonesdev.sonar.api.antibot.ChannelPipelines.*;
|
||||
|
||||
@Getter
|
||||
@ToString(of = {"protocolVersion", "inetAddress", "geyser"})
|
||||
public final class FallbackUserWrapper implements FallbackUser {
|
||||
public final class UserWrapper implements SonarUser {
|
||||
@Accessors(fluent = true)
|
||||
private final Channel channel;
|
||||
private final InetAddress inetAddress;
|
||||
@@ -60,12 +60,12 @@ public final class FallbackUserWrapper implements FallbackUser {
|
||||
@Setter
|
||||
private boolean forceCaptcha;
|
||||
|
||||
public FallbackUserWrapper(final @NotNull ChannelHandlerContext ctx,
|
||||
final @NotNull InetAddress inetAddress,
|
||||
final @NotNull ProtocolVersion protocolVersion,
|
||||
final @NotNull String username,
|
||||
final @NotNull String fingerprint,
|
||||
final boolean geyser) {
|
||||
public UserWrapper(final @NotNull ChannelHandlerContext ctx,
|
||||
final @NotNull InetAddress inetAddress,
|
||||
final @NotNull ProtocolVersion protocolVersion,
|
||||
final @NotNull String username,
|
||||
final @NotNull String fingerprint,
|
||||
final boolean geyser) {
|
||||
this.channel = ctx.channel();
|
||||
this.inetAddress = inetAddress;
|
||||
this.protocolVersion = protocolVersion;
|
||||
@@ -96,43 +96,49 @@ public final class FallbackUserWrapper implements FallbackUser {
|
||||
return;
|
||||
}
|
||||
|
||||
// How? Is there some kind of de-sync or race condition?
|
||||
if (channel.pipeline().context(SONAR_FRAME_ENCODER) != null) {
|
||||
channel.close(); // Nope ¯\_(ツ)_/¯
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark the player as connected by caching them in a map of verifying players
|
||||
Sonar.get0().getFallback().getConnected().compute(inetAddress, (__, v) -> true);
|
||||
Sonar.get0().getAntiBot().getConnected().compute(inetAddress, (__, v) -> true);
|
||||
|
||||
// Replace normal encoder to allow custom packets
|
||||
final FallbackPacketEncoder newEncoder = new FallbackPacketEncoder(protocolVersion);
|
||||
channel.pipeline().addFirst(FALLBACK_FRAME_ENCODER, FallbackVarIntLengthEncoder.INSTANCE);
|
||||
channel.pipeline().addLast(FALLBACK_PACKET_ENCODER, newEncoder);
|
||||
final SonarPacketEncoder newEncoder = new SonarPacketEncoder(protocolVersion);
|
||||
channel.pipeline().addFirst(SONAR_FRAME_ENCODER, MinecraftVarIntLengthEncoder.INSTANCE);
|
||||
channel.pipeline().addLast(SONAR_PACKET_ENCODER, newEncoder);
|
||||
|
||||
// Replace normal decoder to allow custom packets
|
||||
final FallbackPacketDecoder newDecoder = new FallbackPacketDecoder(protocolVersion);
|
||||
channel.pipeline().addFirst(FALLBACK_FRAME_DECODER, new FallbackVarInt21FrameDecoder());
|
||||
channel.pipeline().addLast(FALLBACK_PACKET_DECODER, newDecoder);
|
||||
final SonarPacketDecoder newDecoder = new SonarPacketDecoder(protocolVersion);
|
||||
channel.pipeline().addFirst(SONAR_FRAME_DECODER, new MinecraftVarInt21FrameDecoder());
|
||||
channel.pipeline().addLast(SONAR_PACKET_DECODER, newDecoder);
|
||||
|
||||
// We're sending the LoginSuccess packet now
|
||||
newDecoder.updateRegistry(FallbackPacketRegistry.LOGIN);
|
||||
newEncoder.updateRegistry(FallbackPacketRegistry.LOGIN);
|
||||
newDecoder.updateRegistry(SonarPacketRegistry.LOGIN);
|
||||
newEncoder.updateRegistry(SonarPacketRegistry.LOGIN);
|
||||
// Send LoginSuccess packet to make the client think they are joining the server
|
||||
write(FallbackPreparer.loginSuccess);
|
||||
write(SonarPacketPreparer.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);
|
||||
newDecoder.updateRegistry(SonarPacketRegistry.GAME);
|
||||
newEncoder.updateRegistry(SonarPacketRegistry.GAME);
|
||||
}
|
||||
|
||||
// Listen for all incoming packets by setting the packet listener
|
||||
newDecoder.setListener(new FallbackPreJoinHandler(this));
|
||||
newDecoder.setListener(new LoginHandler(this));
|
||||
|
||||
// Make sure to catch all exceptions during the verification
|
||||
channel.pipeline().addLast(FALLBACK_TAIL_EXCEPTIONS, FallbackTailExceptionsHandler.INSTANCE);
|
||||
channel.pipeline().addLast(SONAR_TAIL_EXCEPTIONS, TailExceptionsHandler.INSTANCE);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(final @NotNull Component reason) {
|
||||
final FallbackPacketEncoder encoder = channel.pipeline().get(FallbackPacketEncoder.class);
|
||||
final boolean duringLogin = encoder != null && encoder.getPacketRegistry() == FallbackPacketRegistry.LOGIN;
|
||||
final SonarPacketEncoder encoder = channel.pipeline().get(SonarPacketEncoder.class);
|
||||
final boolean duringLogin = encoder != null && encoder.getPacketRegistry() == SonarPacketRegistry.LOGIN;
|
||||
ProtocolUtil.closeWith(channel, protocolVersion, new DisconnectPacket(reason, duringLogin));
|
||||
}
|
||||
}
|
||||
@@ -36,9 +36,9 @@ import xyz.jonesdev.sonar.api.notification.ActionBarNotificationHandler;
|
||||
import xyz.jonesdev.sonar.api.notification.ChatNotificationHandler;
|
||||
import xyz.jonesdev.sonar.api.statistics.SonarStatistics;
|
||||
import xyz.jonesdev.sonar.api.timer.SystemTimer;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPreparer;
|
||||
import xyz.jonesdev.sonar.common.fallback.ratelimit.CaffeineCacheRatelimiter;
|
||||
import xyz.jonesdev.sonar.common.fallback.ratelimit.NoopCacheRatelimiter;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacketPreparer;
|
||||
import xyz.jonesdev.sonar.common.ratelimit.CaffeineCacheRatelimiter;
|
||||
import xyz.jonesdev.sonar.common.ratelimit.NoopCacheRatelimiter;
|
||||
import xyz.jonesdev.sonar.common.service.ScheduledServiceManager;
|
||||
import xyz.jonesdev.sonar.common.statistics.GlobalSonarStatistics;
|
||||
import xyz.jonesdev.sonar.common.subcommand.*;
|
||||
@@ -137,26 +137,26 @@ public abstract class SonarBootstrap<T> implements Sonar {
|
||||
|
||||
// Prepare cached packets
|
||||
getLogger().info("Taking cached snapshots of all packets...");
|
||||
FallbackPreparer.prepare();
|
||||
SonarPacketPreparer.prepare();
|
||||
|
||||
// Update ratelimiter cache
|
||||
getFallback().setRatelimiter(getConfig().getVerification().getReconnectDelay() > 0L
|
||||
getAntiBot().setRatelimiter(getConfig().getVerification().getReconnectDelay() > 0L
|
||||
? new CaffeineCacheRatelimiter(Duration.ofMillis(getConfig().getVerification().getReconnectDelay()))
|
||||
: NoopCacheRatelimiter.INSTANCE);
|
||||
|
||||
// Update blacklist cache
|
||||
final long blacklistTime = getConfig().getVerification().getBlacklistTime();
|
||||
final boolean blacklistExists = getFallback().getBlacklist() != null;
|
||||
final boolean blacklistExists = getAntiBot().getBlacklist() != null;
|
||||
// Make sure the blacklist is only set when we need it to prevent data loss
|
||||
if (!blacklistExists // Make sure we create a new cache if it doesn't exist yet
|
||||
|| getFallback().getBlacklistTime() != blacklistTime) {
|
||||
|| getAntiBot().getBlacklistTime() != blacklistTime) {
|
||||
// Create a new cache with the configured blacklist time
|
||||
getFallback().setBlacklist(Caffeine.newBuilder()
|
||||
getAntiBot().setBlacklist(Caffeine.newBuilder()
|
||||
.expireAfterWrite(Duration.ofMillis(getConfig().getVerification().getBlacklistTime()))
|
||||
.ticker(Ticker.systemTicker())
|
||||
.build());
|
||||
// Store the new blacklist time, so we don't have to reset the blacklist every reload
|
||||
getFallback().setBlacklistTime(blacklistTime);
|
||||
getAntiBot().setBlacklistTime(blacklistTime);
|
||||
// Warn the user about changing the expiry of the blacklist values
|
||||
if (blacklistExists) {
|
||||
getLogger().warn("The blacklist has been reset as the duration of entries has changed.");
|
||||
|
||||
-284
@@ -1,284 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Sonar Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
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.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;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.play.*;
|
||||
import xyz.jonesdev.sonar.common.util.ComponentHolder;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
@UtilityClass
|
||||
public class FallbackPreparer {
|
||||
private final Random RANDOM = new Random();
|
||||
|
||||
public static final int PLAYER_ENTITY_ID = RANDOM.nextInt();
|
||||
public static final int VEHICLE_ENTITY_ID = RANDOM.nextInt();
|
||||
|
||||
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 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, 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 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_5);
|
||||
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[] REGISTRY_SYNC_1_21_4 = RegistryDataPacket.of(DimensionRegistry.CODEC_1_21_4);
|
||||
public final FallbackPacket START_WRITING_CHUNKS = new GameEventPacket(13, 0);
|
||||
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 FallbackPacket loginSuccess;
|
||||
public FallbackPacket welcomeMessage;
|
||||
public FallbackPacket enterCodeMessage;
|
||||
public FallbackPacket incorrectCaptcha;
|
||||
public static FallbackPacket joinGame;
|
||||
public FallbackPacket defaultSpawnPosition;
|
||||
public FallbackPacket spawnPosition;
|
||||
public FallbackPacket fallStartPosition;
|
||||
public FallbackPacket fallStartPositionLegacy;
|
||||
public static FallbackPacket transferToOrigin;
|
||||
public FallbackPacket blacklisted;
|
||||
public FallbackPacket alreadyQueued;
|
||||
public FallbackPacket alreadyVerifying;
|
||||
public FallbackPacket reconnectedTooFast;
|
||||
public FallbackPacket protocolBlacklisted;
|
||||
public FallbackPacket unsupportedVersion;
|
||||
public FallbackPacket tooManyOnlinePerIP;
|
||||
public FallbackPacket[] xpCountdown;
|
||||
public FallbackPacket updateTime;
|
||||
|
||||
public static int maxMovementTick, dynamicSpawnYPosition, maxTotalPacketsSent;
|
||||
|
||||
public void prepare() {
|
||||
// Preload the packet registry to avoid CPU/RAM issues on 1st connection
|
||||
Sonar.get0().getLogger().info("Preloading all registered packets...");
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
FallbackPacketRegistry.values();
|
||||
|
||||
// Prepare LoginSuccess packet with capped username to 16 characters
|
||||
final UUID uuid = UUID.randomUUID();
|
||||
String username = Sonar.get0().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.get0().getConfig().getVerification().getGamemode().getId(),
|
||||
-1, 0, 0,
|
||||
RANDOM.nextInt(3), 1, 0, 0,
|
||||
new String[]{"minecraft:overworld"}, "minecraft:overworld", "flat",
|
||||
DimensionType.OVERWORLD, RANDOM.nextLong() & 1337,
|
||||
false, true, false,
|
||||
false, false, false, true));
|
||||
|
||||
// Prepare the gravity check
|
||||
maxMovementTick = Sonar.get0().getConfig().getVerification().getGravity().getMaxMovementTicks();
|
||||
|
||||
double motionY = 0, fallDistance = 0;
|
||||
|
||||
for (int i = 0; i < maxMovementTick; i++) {
|
||||
motionY = (motionY - 0.1) * 0.98f;
|
||||
fallDistance += Math.abs(motionY);
|
||||
}
|
||||
|
||||
// Set the dynamic block and collide Y position based on the maximum fall distance
|
||||
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, 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, false, true));
|
||||
fallStartPositionLegacy = new FallbackPacketSnapshot(new SetPlayerPositionRotationPacket(
|
||||
SPAWN_X_POSITION, dynamicSpawnYPosition, SPAWN_Z_POSITION,
|
||||
0, -90, 0, 0, false, false, true));
|
||||
|
||||
// Prepare collision platform positions
|
||||
final int length = BLOCKS_PER_ROW / 2;
|
||||
for (int i = 0; i < BLOCKS_PACKETS.length; i++) {
|
||||
final BlockUpdate[] blocks = new BlockUpdate[BLOCKS_PER_ROW * BLOCKS_PER_ROW];
|
||||
|
||||
int index = 0;
|
||||
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, PLATFORM_Y_POSITION, z + length, 0, 0);
|
||||
blocks[index++] = new BlockUpdate(position, POSSIBLE_BLOCK_TYPES[i]);
|
||||
}
|
||||
}
|
||||
BLOCKS_PACKETS[i] = new FallbackPacketSnapshot(new UpdateSectionBlocksPacket(0, 0, blocks));
|
||||
}
|
||||
|
||||
// Prepare disconnect packets during login
|
||||
blacklisted = new FallbackPacketSnapshot(new DisconnectPacket(Sonar.get0().getConfig().getVerification().getBlacklisted(), true));
|
||||
alreadyVerifying = new FallbackPacketSnapshot(new DisconnectPacket(Sonar.get0().getConfig().getVerification().getAlreadyVerifying(), true));
|
||||
alreadyQueued = new FallbackPacketSnapshot(new DisconnectPacket(Sonar.get0().getConfig().getVerification().getAlreadyQueued(), true));
|
||||
protocolBlacklisted = new FallbackPacketSnapshot(new DisconnectPacket(Sonar.get0().getConfig().getVerification().getProtocolBlacklisted(), true));
|
||||
reconnectedTooFast = new FallbackPacketSnapshot(new DisconnectPacket(Sonar.get0().getConfig().getVerification().getTooFastReconnect(), true));
|
||||
unsupportedVersion = new FallbackPacketSnapshot(new DisconnectPacket(Sonar.get0().getConfig().getVerification().getUnsupportedVersion(), true));
|
||||
tooManyOnlinePerIP = new FallbackPacketSnapshot(new DisconnectPacket(Sonar.get0().getConfig().getTooManyOnlinePerIp(), true));
|
||||
|
||||
// Prepare transfer packet
|
||||
if (Sonar.get0().getConfig().getGeneralConfig().getBoolean("verification.transfer.enabled")) {
|
||||
transferToOrigin = new FallbackPacketSnapshot(new TransferPacket(
|
||||
Sonar.get0().getConfig().getGeneralConfig().getString("verification.transfer.destination-host"),
|
||||
Sonar.get0().getConfig().getGeneralConfig().getInt("verification.transfer.destination-port")));
|
||||
} else {
|
||||
transferToOrigin = null;
|
||||
}
|
||||
|
||||
// Prepare update time packet
|
||||
final int timeOfDay = Sonar.get0().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.get0().getConfig().getMessagesConfig().getString("verification.welcome");
|
||||
if (welcome.isEmpty()) {
|
||||
welcomeMessage = null;
|
||||
} else {
|
||||
welcomeMessage = new FallbackPacketSnapshot(new SystemChatPacket(new ComponentHolder(
|
||||
MiniMessage.miniMessage().deserialize(welcome,
|
||||
Placeholder.component("prefix", Sonar.get0().getConfig().getPrefix())))));
|
||||
}
|
||||
|
||||
if (Sonar.get0().getConfig().getVerification().getMap().getTiming() != SonarConfiguration.Verification.Timing.NEVER
|
||||
|| Sonar.get0().getConfig().getVerification().getGravity().isCaptchaOnFail()) {
|
||||
// Prepare CAPTCHA messages
|
||||
enterCodeMessage = new FallbackPacketSnapshot(new SystemChatPacket(new ComponentHolder(
|
||||
MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get0().getConfig().getMessagesConfig().getString("verification.captcha.enter"),
|
||||
Placeholder.component("prefix", Sonar.get0().getConfig().getPrefix())))));
|
||||
incorrectCaptcha = new FallbackPacketSnapshot(new SystemChatPacket(new ComponentHolder(
|
||||
MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get0().getConfig().getMessagesConfig().getString("verification.captcha.incorrect"),
|
||||
Placeholder.component("prefix", Sonar.get0().getConfig().getPrefix())))));
|
||||
|
||||
// Prepare countdown
|
||||
xpCountdown = new FallbackPacket[Sonar.get0().getConfig().getVerification().getMap().getMaxDuration() / 1000];
|
||||
|
||||
for (int i = 0; i < xpCountdown.length; i++) {
|
||||
final float bar = (float) i / xpCountdown.length;
|
||||
xpCountdown[i] = new FallbackPacketSnapshot(new SetExperiencePacket(bar, i, 0));
|
||||
}
|
||||
|
||||
// Update the CAPTCHA generator if necessary
|
||||
final CaptchaGenerationStartEvent generationStartEvent = new CaptchaGenerationStartEvent(
|
||||
Sonar.get0().getFallback().getCaptchaGenerator());
|
||||
Sonar.get0().getEventManager().publish(generationStartEvent);
|
||||
|
||||
if (generationStartEvent.getCaptchaGenerator() == null
|
||||
|| generationStartEvent.getCaptchaGenerator() instanceof StandardCaptchaGenerator) {
|
||||
Sonar.get0().getFallback().setCaptchaGenerator(new StandardCaptchaGenerator(
|
||||
Sonar.get0().getConfig().getVerification().getMap().getBackgroundImage()));
|
||||
} else {
|
||||
Sonar.get0().getLogger().info("Custom CAPTCHA generator detected, skipping reinitialization.");
|
||||
}
|
||||
|
||||
// Prepare CAPTCHA answers
|
||||
CaptchaPreparer.prepare();
|
||||
} else {
|
||||
// Throw away if not needed
|
||||
enterCodeMessage = null;
|
||||
incorrectCaptcha = null;
|
||||
xpCountdown = null;
|
||||
Sonar.get0().getFallback().setCaptchaGenerator(null);
|
||||
}
|
||||
|
||||
maxTotalPacketsSent = maxMovementTick + 2
|
||||
+ (xpCountdown == null ? 0 : xpCountdown.length) * 20 + 5
|
||||
+ Sonar.get0().getConfig().getVerification().getVehicle().getMinimumPackets() * 4
|
||||
+ Sonar.get0().getConfig().getVerification().getMap().getMaxTries()
|
||||
+ 150 /* some arbitrary leeway */;
|
||||
}
|
||||
|
||||
public static FallbackPacket[] getRegistryPackets(final @NotNull ProtocolVersion protocolVersion) {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_4)) {
|
||||
return REGISTRY_SYNC_1_21_4;
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_2)) {
|
||||
return REGISTRY_SYNC_1_21_2;
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21)) {
|
||||
return REGISTRY_SYNC_1_21;
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_20_5)) {
|
||||
return REGISTRY_SYNC_1_20_5;
|
||||
}
|
||||
return REGISTRY_SYNC_1_20;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.netty;
|
||||
package xyz.jonesdev.sonar.common.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
@@ -29,7 +29,7 @@ import xyz.jonesdev.sonar.common.util.exception.QuietDecoderException;
|
||||
import java.util.List;
|
||||
|
||||
// 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 {
|
||||
public final class MinecraftVarInt21FrameDecoder extends ByteToMessageDecoder {
|
||||
|
||||
@Override
|
||||
protected void decode(final @NotNull ChannelHandlerContext ctx,
|
||||
+3
-3
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.netty;
|
||||
package xyz.jonesdev.sonar.common.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
@@ -28,8 +28,8 @@ import java.util.List;
|
||||
|
||||
// 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> {
|
||||
public static final FallbackVarIntLengthEncoder INSTANCE = new FallbackVarIntLengthEncoder();
|
||||
public final class MinecraftVarIntLengthEncoder extends MessageToMessageEncoder<ByteBuf> {
|
||||
public static final MinecraftVarIntLengthEncoder INSTANCE = new MinecraftVarIntLengthEncoder();
|
||||
|
||||
@Override
|
||||
protected void encode(final @NotNull ChannelHandlerContext ctx,
|
||||
+15
-7
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.netty;
|
||||
package xyz.jonesdev.sonar.common.netty;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
@@ -24,7 +24,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.common.fallback.FallbackInboundHandler;
|
||||
import xyz.jonesdev.sonar.common.InboundHandler;
|
||||
import xyz.jonesdev.sonar.common.util.FakeChannelUtil;
|
||||
import xyz.jonesdev.sonar.common.util.exception.ReflectiveOperationException;
|
||||
|
||||
@@ -33,10 +33,10 @@ import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.FallbackPipelines.FALLBACK_INBOUND_HANDLER;
|
||||
import static xyz.jonesdev.sonar.api.antibot.ChannelPipelines.SONAR_INBOUND_HANDLER;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class FallbackInjectedChannelInitializer extends ChannelInitializer<Channel> {
|
||||
public final class SonarInjectedChannelInitializer extends ChannelInitializer<Channel> {
|
||||
private static final MethodHandle INIT_CHANNEL_METHOD;
|
||||
|
||||
static {
|
||||
@@ -53,6 +53,10 @@ public final class FallbackInjectedChannelInitializer extends ChannelInitializer
|
||||
|
||||
@Override
|
||||
protected void initChannel(final Channel channel) throws Exception {
|
||||
if (!channel.isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Invoke the original method
|
||||
try {
|
||||
INIT_CHANNEL_METHOD.invokeExact(originalChannelInitializer, channel);
|
||||
@@ -60,16 +64,20 @@ public final class FallbackInjectedChannelInitializer extends ChannelInitializer
|
||||
throw new ReflectiveOperationException(throwable);
|
||||
}
|
||||
|
||||
inject(channel, sonarPipelineInjector);
|
||||
}
|
||||
|
||||
public static void inject(final @NotNull Channel channel, final Consumer<ChannelPipeline> sonarPipelineInjector) {
|
||||
// Inject Sonar's channel handler into the pipeline;
|
||||
// Also make sure the player is not a fake player to avoid compatibility issues
|
||||
if (channel.isActive() && !FakeChannelUtil.isFakeChannel(channel)) {
|
||||
final FallbackInboundHandler inboundHandler = new FallbackInboundHandler(sonarPipelineInjector);
|
||||
final InboundHandler inboundHandler = new InboundHandler(sonarPipelineInjector);
|
||||
// We need to be careful on Bukkit, as the encoder can be different
|
||||
if (Sonar.get0().getPlatform() == SonarPlatform.BUKKIT) {
|
||||
final String encoder = Sonar.get0().getPlatform().getEncoder().apply(channel.pipeline());
|
||||
channel.pipeline().addBefore(encoder, FALLBACK_INBOUND_HANDLER, inboundHandler);
|
||||
channel.pipeline().addBefore(encoder, SONAR_INBOUND_HANDLER, inboundHandler);
|
||||
} else {
|
||||
channel.pipeline().addFirst(FALLBACK_INBOUND_HANDLER, inboundHandler);
|
||||
channel.pipeline().addFirst(SONAR_INBOUND_HANDLER, inboundHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.netty;
|
||||
package xyz.jonesdev.sonar.common.netty;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
@@ -24,8 +24,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public final class FallbackTimeoutHandler extends IdleStateHandler {
|
||||
public FallbackTimeoutHandler(final int readTimeout, final int writeTimeout, final TimeUnit timeUnit) {
|
||||
public final class SonarTimeoutHandler extends IdleStateHandler {
|
||||
public SonarTimeoutHandler(final int readTimeout, final int writeTimeout, final TimeUnit timeUnit) {
|
||||
super(false, readTimeout, writeTimeout, 0L, timeUnit);
|
||||
}
|
||||
|
||||
+3
-3
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.netty;
|
||||
package xyz.jonesdev.sonar.common.netty;
|
||||
|
||||
import io.netty.channel.ChannelDuplexHandler;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
@@ -23,8 +23,8 @@ import io.netty.channel.ChannelHandlerContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ChannelHandler.Sharable
|
||||
public final class FallbackTailExceptionsHandler extends ChannelDuplexHandler {
|
||||
public static final FallbackTailExceptionsHandler INSTANCE = new FallbackTailExceptionsHandler();
|
||||
public final class TailExceptionsHandler extends ChannelDuplexHandler {
|
||||
public static final TailExceptionsHandler INSTANCE = new TailExceptionsHandler();
|
||||
|
||||
// We can override the default exceptionCaught method since the server
|
||||
// does not have any other pipelines that could handle this error.
|
||||
+4
-4
@@ -15,14 +15,14 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol;
|
||||
package xyz.jonesdev.sonar.common.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 xyz.jonesdev.sonar.common.protocol.map.MapCaptchaInfo;
|
||||
import xyz.jonesdev.sonar.common.protocol.map.MapColorPalette;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.Random;
|
||||
@@ -62,7 +62,7 @@ public class CaptchaPreparer {
|
||||
for (int j = 0; j < answer.length; j++) {
|
||||
answer[j] = alphabet[RANDOM.nextInt(alphabet.length)];
|
||||
}
|
||||
final BufferedImage image = Sonar.get0().getFallback().getCaptchaGenerator().createImage(answer);
|
||||
final BufferedImage image = Sonar.get0().getAntiBot().getCaptchaGenerator().createImage(answer);
|
||||
// Convert and cache converted Minecraft map bytes
|
||||
cached[preparedAmount++] = new MapCaptchaInfo(new String(answer), MapColorPalette.imageToBuffer(image));
|
||||
}
|
||||
+3
-3
@@ -15,12 +15,12 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol;
|
||||
package xyz.jonesdev.sonar.common.protocol;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
|
||||
public interface FallbackPacket {
|
||||
public interface SonarPacket {
|
||||
|
||||
/**
|
||||
* Encodes the packet sent by the server
|
||||
+10
-10
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol;
|
||||
package xyz.jonesdev.sonar.common.protocol;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
@@ -24,22 +24,22 @@ 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.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
import xyz.jonesdev.sonar.common.util.exception.QuietDecoderException;
|
||||
|
||||
import static xyz.jonesdev.sonar.common.fallback.protocol.FallbackPreparer.maxTotalPacketsSent;
|
||||
import static xyz.jonesdev.sonar.common.protocol.SonarPacketPreparer.maxTotalPacketsSent;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class FallbackPacketDecoder extends ChannelInboundHandlerAdapter {
|
||||
public final class SonarPacketDecoder extends ChannelInboundHandlerAdapter {
|
||||
private final ProtocolVersion protocolVersion;
|
||||
private FallbackPacketRegistry.ProtocolRegistry registry;
|
||||
private SonarPacketRegistry.ProtocolRegistry registry;
|
||||
@Setter
|
||||
private FallbackPacketListener listener;
|
||||
private SonarPacketListener listener;
|
||||
private int totalPacketsSent;
|
||||
|
||||
public void updateRegistry(final @NotNull FallbackPacketRegistry registry) {
|
||||
this.registry = registry.getProtocolRegistry(FallbackPacketRegistry.Direction.SERVERBOUND, protocolVersion);
|
||||
public void updateRegistry(final @NotNull SonarPacketRegistry registry) {
|
||||
this.registry = registry.getProtocolRegistry(SonarPacketRegistry.Direction.SERVERBOUND, protocolVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,7 +62,7 @@ public final class FallbackPacketDecoder extends ChannelInboundHandlerAdapter {
|
||||
|
||||
// Read the packet ID and then create the packet from it
|
||||
final int packetId = ProtocolUtil.readVarInt(byteBuf);
|
||||
final FallbackPacket packet = registry.createPacket(packetId);
|
||||
final SonarPacket packet = registry.createPacket(packetId);
|
||||
|
||||
// Skip the packet if it's not registered within Sonar's packet registry
|
||||
if (packet == null) {
|
||||
@@ -98,7 +98,7 @@ public final class FallbackPacketDecoder extends ChannelInboundHandlerAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkPacketSize(final int packetSize, final @NotNull FallbackPacket packet) throws Exception {
|
||||
private void checkPacketSize(final int packetSize, final @NotNull SonarPacket packet) throws Exception {
|
||||
final int expectedMaxLen = packet.expectedMaxLength(protocolVersion);
|
||||
if (expectedMaxLen != -1 && packetSize > expectedMaxLen) {
|
||||
throw ProtocolUtil.DEBUG ? new DecoderException("Packet too large: " + packetSize + " max: " + expectedMaxLen)
|
||||
+10
-10
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol;
|
||||
package xyz.jonesdev.sonar.common.protocol;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
@@ -23,27 +23,27 @@ 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.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class FallbackPacketEncoder extends MessageToByteEncoder<FallbackPacket> {
|
||||
public final class SonarPacketEncoder extends MessageToByteEncoder<SonarPacket> {
|
||||
private final ProtocolVersion protocolVersion;
|
||||
@Getter
|
||||
private FallbackPacketRegistry packetRegistry;
|
||||
private FallbackPacketRegistry.ProtocolRegistry protocolRegistry;
|
||||
private SonarPacketRegistry packetRegistry;
|
||||
private SonarPacketRegistry.ProtocolRegistry protocolRegistry;
|
||||
|
||||
public void updateRegistry(final @NotNull FallbackPacketRegistry packetRegistry) {
|
||||
public void updateRegistry(final @NotNull SonarPacketRegistry packetRegistry) {
|
||||
this.packetRegistry = packetRegistry;
|
||||
this.protocolRegistry = packetRegistry.getProtocolRegistry(FallbackPacketRegistry.Direction.CLIENTBOUND, protocolVersion);
|
||||
this.protocolRegistry = packetRegistry.getProtocolRegistry(SonarPacketRegistry.Direction.CLIENTBOUND, protocolVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(final ChannelHandlerContext ctx,
|
||||
final @NotNull FallbackPacket packet,
|
||||
final @NotNull SonarPacket packet,
|
||||
final @NotNull ByteBuf out) throws Exception {
|
||||
final Class<? extends FallbackPacket> originalPacket = packet instanceof FallbackPacketSnapshot
|
||||
? ((FallbackPacketSnapshot) packet).getOriginalPacketClass() : packet.getClass();
|
||||
final Class<? extends SonarPacket> originalPacket = packet instanceof SonarPacketSnapshot
|
||||
? ((SonarPacketSnapshot) packet).getOriginalPacketClass() : packet.getClass();
|
||||
final int packetId = protocolRegistry.getPacketId(originalPacket);
|
||||
ProtocolUtil.writeVarInt(out, packetId);
|
||||
packet.encode(out, protocolVersion);
|
||||
+3
-3
@@ -15,17 +15,17 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol;
|
||||
package xyz.jonesdev.sonar.common.protocol;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface FallbackPacketListener {
|
||||
public interface SonarPacketListener {
|
||||
|
||||
/**
|
||||
* Handles the incoming packet
|
||||
*
|
||||
* @param packet Packet to handle
|
||||
*/
|
||||
void handle(final @NotNull FallbackPacket packet);
|
||||
void handle(final @NotNull SonarPacket packet);
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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.protocol;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
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.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.api.config.SonarConfiguration;
|
||||
import xyz.jonesdev.sonar.api.event.impl.CaptchaGenerationStartEvent;
|
||||
import xyz.jonesdev.sonar.captcha.StandardCaptchaGenerator;
|
||||
import xyz.jonesdev.sonar.captcha.complex.ComplexCaptchaGenerator;
|
||||
import xyz.jonesdev.sonar.captcha.legacy.LegacyCaptchaGenerator;
|
||||
import xyz.jonesdev.sonar.common.protocol.block.BlockType;
|
||||
import xyz.jonesdev.sonar.common.protocol.block.BlockUpdate;
|
||||
import xyz.jonesdev.sonar.common.protocol.dimension.DimensionRegistry;
|
||||
import xyz.jonesdev.sonar.common.protocol.dimension.DimensionType;
|
||||
import xyz.jonesdev.sonar.common.protocol.entity.EntityType;
|
||||
import xyz.jonesdev.sonar.common.protocol.item.ItemType;
|
||||
import xyz.jonesdev.sonar.common.protocol.packets.configuration.RegistryDataPacket;
|
||||
import xyz.jonesdev.sonar.common.protocol.packets.login.LoginSuccessPacket;
|
||||
import xyz.jonesdev.sonar.common.protocol.packets.play.*;
|
||||
import xyz.jonesdev.sonar.common.util.ComponentHolder;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
@UtilityClass
|
||||
public class SonarPacketPreparer {
|
||||
private final Random RANDOM = new Random();
|
||||
|
||||
public static final int PLAYER_ENTITY_ID = RANDOM.nextInt();
|
||||
public static final int VEHICLE_ENTITY_ID = RANDOM.nextInt();
|
||||
|
||||
public static final BlockType[] POSSIBLE_BLOCK_TYPES = BlockType.values();
|
||||
public final SonarPacket[] BLOCKS_PACKETS = new SonarPacket[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 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 SonarPacket 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 SonarPacket CAPTCHA_HELD_ITEM_SLOT = new SetHeldItemPacket(MAP_SLOT);
|
||||
public final SonarPacket DEFAULT_ABILITIES = new PlayerAbilitiesPacket(0x00, 0, 0);
|
||||
public final SonarPacket NO_MOVE_ABILITIES = new PlayerAbilitiesPacket(0x02, 0, 0);
|
||||
public final SonarPacket NO_MOVE_ABILITIES_BEDROCK = new PlayerAbilitiesPacket(0x06, 0, 0);
|
||||
public final SonarPacket CAPTCHA_POSITION = new SonarPacketSnapshot(new SetPlayerPositionRotationPacket(
|
||||
SPAWN_X_POSITION, 10000, SPAWN_Z_POSITION, 0, 90, 0, 0, false, false, true));
|
||||
// I think the people working at Microsoft are actually the *real* ultimate trolls of the internet!
|
||||
public final SonarPacket[] EMPTY_CHUNK_DATA = new SonarPacket[]{
|
||||
new SonarPacketSnapshot(new ChunkDataPacket(0, 0)),
|
||||
new SonarPacketSnapshot(new ChunkDataPacket(0, 1)),
|
||||
new SonarPacketSnapshot(new ChunkDataPacket(0, -1)),
|
||||
new SonarPacketSnapshot(new ChunkDataPacket(1, 0)),
|
||||
new SonarPacketSnapshot(new ChunkDataPacket(1, 1)),
|
||||
new SonarPacketSnapshot(new ChunkDataPacket(1, -1)),
|
||||
new SonarPacketSnapshot(new ChunkDataPacket(-1, 0)),
|
||||
new SonarPacketSnapshot(new ChunkDataPacket(-1, 1)),
|
||||
new SonarPacketSnapshot(new ChunkDataPacket(-1, -1))
|
||||
};
|
||||
public final SonarPacket PRE_JOIN_KEEP_ALIVE = new SonarPacketSnapshot(new KeepAlivePacket(PRE_JOIN_KEEP_ALIVE_ID));
|
||||
public final SonarPacket[] REGISTRY_SYNC_1_20 = new SonarPacket[] {
|
||||
new SonarPacketSnapshot(new RegistryDataPacket(DimensionRegistry.CODEC_1_20, null, null))};
|
||||
public final SonarPacket[] REGISTRY_SYNC_1_20_5 = RegistryDataPacket.of(DimensionRegistry.CODEC_1_20_5);
|
||||
public final SonarPacket[] REGISTRY_SYNC_1_21 = RegistryDataPacket.of(DimensionRegistry.CODEC_1_21);
|
||||
public final SonarPacket[] REGISTRY_SYNC_1_21_2 = RegistryDataPacket.of(DimensionRegistry.CODEC_1_21_2);
|
||||
public final SonarPacket[] REGISTRY_SYNC_1_21_4 = RegistryDataPacket.of(DimensionRegistry.CODEC_1_21_4);
|
||||
public final SonarPacket[] REGISTRY_SYNC_1_21_5 = RegistryDataPacket.of(DimensionRegistry.CODEC_1_21_5);
|
||||
public final SonarPacket[] REGISTRY_SYNC_1_21_11 = RegistryDataPacket.of(DimensionRegistry.CODEC_1_21_11);
|
||||
public final SonarPacket START_WRITING_CHUNKS = new GameEventPacket(13, 0);
|
||||
public final static SonarPacket INVALID_HELD_ITEM_SLOT = new SetHeldItemPacket(-1);
|
||||
public final SonarPacket RANDOM_KEEP_ALIVE = new SonarPacketSnapshot(new KeepAlivePacket(RANDOM.nextInt()));
|
||||
public static final SonarPacket SPAWN_BOAT_ENTITY = new SonarPacketSnapshot(new SpawnEntityPacket(
|
||||
VEHICLE_ENTITY_ID, EntityType.BOAT, SPAWN_X_POSITION, IN_AIR_Y_POSITION, SPAWN_Z_POSITION,
|
||||
0, 0, 0, 0));
|
||||
public static final SonarPacket SPAWN_MINECART_ENTITY = new SonarPacketSnapshot(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 SonarPacket REMOVE_VEHICLE = new RemoveEntitiesPacket(VEHICLE_ENTITY_ID);
|
||||
public static final SonarPacket SET_VEHICLE_PASSENGERS = new SonarPacketSnapshot(
|
||||
new SetPassengersPacket(VEHICLE_ENTITY_ID, PLAYER_ENTITY_ID));
|
||||
public SonarPacket[] incorrectCaptcha;
|
||||
|
||||
public SonarPacket loginSuccess;
|
||||
public SonarPacket welcomeMessage;
|
||||
public SonarPacket enterCodeMessage;
|
||||
public static SonarPacket joinGame;
|
||||
public SonarPacket defaultSpawnPosition;
|
||||
public SonarPacket spawnPosition;
|
||||
public SonarPacket fallStartPosition;
|
||||
public SonarPacket fallStartPositionLegacy;
|
||||
public static SonarPacket transferToOrigin;
|
||||
public SonarPacket blacklisted;
|
||||
public SonarPacket alreadyQueued;
|
||||
public SonarPacket alreadyVerifying;
|
||||
public SonarPacket reconnectedTooFast;
|
||||
public SonarPacket protocolBlacklisted;
|
||||
public SonarPacket unsupportedVersion;
|
||||
public SonarPacket tooManyOnlinePerIP;
|
||||
public SonarPacket[] xpCountdown;
|
||||
public SonarPacket updateTime;
|
||||
|
||||
public static int maxMovementTick, dynamicSpawnYPosition, maxTotalPacketsSent;
|
||||
|
||||
public void prepare() {
|
||||
// Preload the packet registry to avoid CPU/RAM issues on 1st connection
|
||||
Sonar.get0().getLogger().info("Preloading all registered packets...");
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
SonarPacketRegistry.values();
|
||||
|
||||
// Prepare LoginSuccess packet with capped username to 16 characters
|
||||
final UUID uuid = UUID.randomUUID();
|
||||
String username = Sonar.get0().getConfig().getGeneralConfig().getString("verification.cached-username");
|
||||
if (username.length() > 16) {
|
||||
username = username.substring(0, 16);
|
||||
}
|
||||
loginSuccess = new SonarPacketSnapshot(new LoginSuccessPacket(uuid, username, true));
|
||||
|
||||
// Prepare JoinGame packet
|
||||
joinGame = new SonarPacketSnapshot(new JoinGamePacket(PLAYER_ENTITY_ID,
|
||||
Sonar.get0().getConfig().getVerification().getGamemode().getId(),
|
||||
-1, 0, 0,
|
||||
RANDOM.nextInt(3), 1, 0, 0,
|
||||
new String[]{"minecraft:overworld"}, "minecraft:overworld", "flat",
|
||||
DimensionType.OVERWORLD, RANDOM.nextLong() & 1337,
|
||||
false, true, false,
|
||||
false, false, false, true));
|
||||
|
||||
// Prepare the gravity check
|
||||
maxMovementTick = Sonar.get0().getConfig().getVerification().getGravity().getMaxMovementTicks();
|
||||
|
||||
double motionY = 0, fallDistance = 0;
|
||||
|
||||
for (int i = 0; i < maxMovementTick; i++) {
|
||||
motionY = (motionY - 0.1) * 0.98f;
|
||||
fallDistance += Math.abs(motionY);
|
||||
}
|
||||
|
||||
// Set the dynamic block and collide Y position based on the maximum fall distance
|
||||
dynamicSpawnYPosition = PLATFORM_Y_POSITION + (int) Math.ceil(fallDistance);
|
||||
defaultSpawnPosition = new SonarPacketSnapshot(new SetDefaultSpawnPositionPacket(
|
||||
"minecraft:overworld",
|
||||
SPAWN_X_POSITION, IN_AIR_Y_POSITION, SPAWN_Z_POSITION));
|
||||
spawnPosition = new SonarPacketSnapshot(new SetPlayerPositionRotationPacket(
|
||||
SPAWN_X_POSITION, IN_AIR_Y_POSITION, SPAWN_Z_POSITION,
|
||||
0, 0, FIRST_TELEPORT_ID, 0, false, false, true));
|
||||
fallStartPosition = new SonarPacketSnapshot(new SetPlayerPositionRotationPacket(
|
||||
SPAWN_X_POSITION, dynamicSpawnYPosition - IN_AIR_Y_POSITION, SPAWN_Z_POSITION,
|
||||
0, -90, SECOND_TELEPORT_ID, 1 << 1, false, false, true));
|
||||
fallStartPositionLegacy = new SonarPacketSnapshot(new SetPlayerPositionRotationPacket(
|
||||
SPAWN_X_POSITION, dynamicSpawnYPosition, SPAWN_Z_POSITION,
|
||||
0, -90, 0, 0, false, false, true));
|
||||
|
||||
// Prepare collision platform positions
|
||||
final int length = BLOCKS_PER_ROW / 2;
|
||||
for (int i = 0; i < BLOCKS_PACKETS.length; i++) {
|
||||
final BlockUpdate[] blocks = new BlockUpdate[BLOCKS_PER_ROW * BLOCKS_PER_ROW];
|
||||
|
||||
int index = 0;
|
||||
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, PLATFORM_Y_POSITION, z + length, 0, 0);
|
||||
blocks[index++] = new BlockUpdate(position, POSSIBLE_BLOCK_TYPES[i]);
|
||||
}
|
||||
}
|
||||
BLOCKS_PACKETS[i] = new SonarPacketSnapshot(new UpdateSectionBlocksPacket(0, 0, blocks));
|
||||
}
|
||||
|
||||
// Prepare disconnect packets during login
|
||||
blacklisted = new SonarPacketSnapshot(new DisconnectPacket(Sonar.get0().getConfig().getVerification().getBlacklisted(), true));
|
||||
alreadyVerifying = new SonarPacketSnapshot(new DisconnectPacket(Sonar.get0().getConfig().getVerification().getAlreadyVerifying(), true));
|
||||
alreadyQueued = new SonarPacketSnapshot(new DisconnectPacket(Sonar.get0().getConfig().getVerification().getAlreadyQueued(), true));
|
||||
protocolBlacklisted = new SonarPacketSnapshot(new DisconnectPacket(Sonar.get0().getConfig().getVerification().getProtocolBlacklisted(), true));
|
||||
reconnectedTooFast = new SonarPacketSnapshot(new DisconnectPacket(Sonar.get0().getConfig().getVerification().getTooFastReconnect(), true));
|
||||
unsupportedVersion = new SonarPacketSnapshot(new DisconnectPacket(Sonar.get0().getConfig().getVerification().getUnsupportedVersion(), true));
|
||||
tooManyOnlinePerIP = new SonarPacketSnapshot(new DisconnectPacket(Sonar.get0().getConfig().getTooManyOnlinePerIp(), true));
|
||||
|
||||
// Prepare transfer packet
|
||||
if (Sonar.get0().getConfig().getGeneralConfig().getBoolean("verification.transfer.enabled")) {
|
||||
transferToOrigin = new SonarPacketSnapshot(new TransferPacket(
|
||||
Sonar.get0().getConfig().getGeneralConfig().getString("verification.transfer.destination-host"),
|
||||
Sonar.get0().getConfig().getGeneralConfig().getInt("verification.transfer.destination-port")));
|
||||
} else {
|
||||
transferToOrigin = null;
|
||||
}
|
||||
|
||||
// Prepare update time packet
|
||||
final int timeOfDay = Sonar.get0().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.get0().getConfig().getMessagesConfig().getString("verification.welcome");
|
||||
if (welcome.isEmpty()) {
|
||||
welcomeMessage = null;
|
||||
} else {
|
||||
welcomeMessage = new SonarPacketSnapshot(new SystemChatPacket(new ComponentHolder(
|
||||
MiniMessage.miniMessage().deserialize(welcome,
|
||||
Placeholder.component("prefix", Sonar.get0().getConfig().getPrefix())))));
|
||||
}
|
||||
|
||||
if (Sonar.get0().getConfig().getVerification().getMap().getTiming() != SonarConfiguration.Verification.Timing.NEVER
|
||||
|| Sonar.get0().getConfig().getVerification().getGravity().isCaptchaOnFail()) {
|
||||
enterCodeMessage = new SonarPacketSnapshot(new SystemChatPacket(new ComponentHolder(
|
||||
MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get0().getConfig().getMessagesConfig().getString("verification.captcha.enter"),
|
||||
Placeholder.component("prefix", Sonar.get0().getConfig().getPrefix())))));
|
||||
incorrectCaptcha = new SonarPacket[Sonar.get0().getConfig().getVerification().getMap().getMaxTries()];
|
||||
for (int i = 0; i < incorrectCaptcha.length; i++) {
|
||||
incorrectCaptcha[i] = new SonarPacketSnapshot(new SystemChatPacket(new ComponentHolder(
|
||||
MiniMessage.miniMessage().deserialize(
|
||||
Sonar.get0().getConfig().getMessagesConfig().getString("verification.captcha.incorrect"),
|
||||
Placeholder.component("prefix", Sonar.get0().getConfig().getPrefix()),
|
||||
Placeholder.unparsed("attempts-left", String.valueOf(i + 1))))));
|
||||
}
|
||||
xpCountdown = new SonarPacket[Sonar.get0().getConfig().getVerification().getMap().getMaxDuration() / 1000];
|
||||
|
||||
for (int i = 0; i < xpCountdown.length; i++) {
|
||||
final float bar = (float) i / xpCountdown.length;
|
||||
xpCountdown[i] = new SonarPacketSnapshot(new SetExperiencePacket(bar, i, 0));
|
||||
}
|
||||
|
||||
final CaptchaGenerationStartEvent generationStartEvent = new CaptchaGenerationStartEvent(
|
||||
Sonar.get0().getAntiBot().getCaptchaGenerator());
|
||||
Sonar.get0().getEventManager().publish(generationStartEvent);
|
||||
|
||||
if (generationStartEvent.getCaptchaGenerator() == null
|
||||
|| generationStartEvent.getCaptchaGenerator() instanceof StandardCaptchaGenerator
|
||||
|| generationStartEvent.getCaptchaGenerator() instanceof LegacyCaptchaGenerator
|
||||
|| generationStartEvent.getCaptchaGenerator() instanceof ComplexCaptchaGenerator) {
|
||||
final File backgroundImage = Sonar.get0().getConfig().getVerification().getMap().getBackgroundImage();
|
||||
switch (Sonar.get0().getConfig().getGeneralConfig()
|
||||
.getString("verification.checks.map-captcha.style").toLowerCase()) {
|
||||
case "legacy":
|
||||
Sonar.get0().getAntiBot().setCaptchaGenerator(new LegacyCaptchaGenerator(backgroundImage));
|
||||
break;
|
||||
case "complex":
|
||||
Sonar.get0().getAntiBot().setCaptchaGenerator(new ComplexCaptchaGenerator(backgroundImage));
|
||||
break;
|
||||
default:
|
||||
Sonar.get0().getAntiBot().setCaptchaGenerator(new StandardCaptchaGenerator(backgroundImage));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
Sonar.get0().getLogger().info("Custom CAPTCHA generator detected, skipping reinitialization.");
|
||||
}
|
||||
|
||||
CaptchaPreparer.prepare();
|
||||
} else {
|
||||
enterCodeMessage = null;
|
||||
incorrectCaptcha = null;
|
||||
xpCountdown = null;
|
||||
Sonar.get0().getAntiBot().setCaptchaGenerator(null);
|
||||
}
|
||||
|
||||
maxTotalPacketsSent = maxMovementTick + 2
|
||||
+ (xpCountdown == null ? 0 : xpCountdown.length) * 20 + 5
|
||||
+ Sonar.get0().getConfig().getVerification().getVehicle().getMinimumPackets() * 4
|
||||
+ Sonar.get0().getConfig().getVerification().getMap().getMaxTries()
|
||||
+ Sonar.get0().getConfig().getVerification().getMaxPacketCount();
|
||||
}
|
||||
|
||||
public static SonarPacket[] getRegistryPackets(final @NotNull ProtocolVersion protocolVersion) {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_11)) {
|
||||
return REGISTRY_SYNC_1_21_11;
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_5)) {
|
||||
return REGISTRY_SYNC_1_21_5;
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_4)) {
|
||||
return REGISTRY_SYNC_1_21_4;
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_2)) {
|
||||
return REGISTRY_SYNC_1_21_2;
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21)) {
|
||||
return REGISTRY_SYNC_1_21;
|
||||
} else if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_20_5)) {
|
||||
return REGISTRY_SYNC_1_20_5;
|
||||
}
|
||||
return REGISTRY_SYNC_1_20;
|
||||
}
|
||||
}
|
||||
+147
-72
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol;
|
||||
package xyz.jonesdev.sonar.common.protocol;
|
||||
|
||||
import io.netty.util.collection.IntObjectHashMap;
|
||||
import io.netty.util.collection.IntObjectMap;
|
||||
@@ -23,14 +23,14 @@ import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
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.handshake.HandshakePacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.login.LoginAcknowledgedPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.login.LoginStartPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.login.LoginSuccessPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.packets.play.*;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.packets.configuration.FinishConfigurationPacket;
|
||||
import xyz.jonesdev.sonar.common.protocol.packets.configuration.RegistryDataPacket;
|
||||
import xyz.jonesdev.sonar.common.protocol.packets.handshake.HandshakePacket;
|
||||
import xyz.jonesdev.sonar.common.protocol.packets.login.LoginAcknowledgedPacket;
|
||||
import xyz.jonesdev.sonar.common.protocol.packets.login.LoginStartPacket;
|
||||
import xyz.jonesdev.sonar.common.protocol.packets.login.LoginSuccessPacket;
|
||||
import xyz.jonesdev.sonar.common.protocol.packets.play.*;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
@@ -38,10 +38,10 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion.*;
|
||||
|
||||
// https://github.com/PaperMC/Velocity/blob/dev/3.0.0/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java
|
||||
public enum FallbackPacketRegistry {
|
||||
public enum SonarPacketRegistry {
|
||||
HANDSHAKE {
|
||||
{
|
||||
serverbound.register(HandshakePacket.class, HandshakePacket::new,
|
||||
@@ -66,7 +66,7 @@ public enum FallbackPacketRegistry {
|
||||
clientbound.register(DisconnectPacket.class, DisconnectPacket::new,
|
||||
map(0x01, MINECRAFT_1_20_2, true),
|
||||
map(0x02, MINECRAFT_1_20_5, true));
|
||||
clientbound.register(FinishConfigurationPacket.class, FinishConfigurationPacket::new,
|
||||
clientbound.register(FinishConfigurationPacket.class, () -> FinishConfigurationPacket.INSTANCE,
|
||||
map(0x02, MINECRAFT_1_20_2, true),
|
||||
map(0x03, MINECRAFT_1_20_5, true));
|
||||
clientbound.register(KeepAlivePacket.class, KeepAlivePacket::new,
|
||||
@@ -81,7 +81,7 @@ public enum FallbackPacketRegistry {
|
||||
serverbound.register(PluginMessagePacket.class, PluginMessagePacket::new,
|
||||
map(0x01, MINECRAFT_1_20_2, false),
|
||||
map(0x02, MINECRAFT_1_20_5, false));
|
||||
serverbound.register(FinishConfigurationPacket.class, FinishConfigurationPacket::new,
|
||||
serverbound.register(FinishConfigurationPacket.class, () -> FinishConfigurationPacket.INSTANCE,
|
||||
map(0x02, MINECRAFT_1_20_2, false),
|
||||
map(0x03, MINECRAFT_1_20_5, false));
|
||||
serverbound.register(KeepAlivePacket.class, KeepAlivePacket::new,
|
||||
@@ -91,6 +91,7 @@ public enum FallbackPacketRegistry {
|
||||
},
|
||||
GAME {
|
||||
{
|
||||
// minecraft:login
|
||||
clientbound.register(JoinGamePacket.class, JoinGamePacket::new,
|
||||
map(0x01, MINECRAFT_1_7_2, true),
|
||||
map(0x23, MINECRAFT_1_9, true),
|
||||
@@ -106,7 +107,10 @@ public enum FallbackPacketRegistry {
|
||||
map(0x28, MINECRAFT_1_19_4, true),
|
||||
map(0x29, MINECRAFT_1_20_2, true),
|
||||
map(0x2B, MINECRAFT_1_20_5, true),
|
||||
map(0x2C, MINECRAFT_1_21_2, true));
|
||||
map(0x2C, MINECRAFT_1_21_2, true),
|
||||
map(0x2B, MINECRAFT_1_21_5, true),
|
||||
map(0x30, MINECRAFT_1_21_9, true));
|
||||
// minecraft:keep_alive
|
||||
clientbound.register(KeepAlivePacket.class, KeepAlivePacket::new,
|
||||
map(0x00, MINECRAFT_1_7_2, true),
|
||||
map(0x1F, MINECRAFT_1_9, true),
|
||||
@@ -122,7 +126,10 @@ public enum FallbackPacketRegistry {
|
||||
map(0x23, MINECRAFT_1_19_4, true),
|
||||
map(0x24, MINECRAFT_1_20_2, true),
|
||||
map(0x26, MINECRAFT_1_20_5, true),
|
||||
map(0x27, MINECRAFT_1_21_2, true));
|
||||
map(0x27, MINECRAFT_1_21_2, true),
|
||||
map(0x26, MINECRAFT_1_21_5, true),
|
||||
map(0x2B, MINECRAFT_1_21_9, true));
|
||||
// minecraft:disconnect
|
||||
clientbound.register(DisconnectPacket.class, DisconnectPacket::new,
|
||||
map(0x40, MINECRAFT_1_7_2, true),
|
||||
map(0x1A, MINECRAFT_1_9, true),
|
||||
@@ -137,7 +144,10 @@ public enum FallbackPacketRegistry {
|
||||
map(0x17, MINECRAFT_1_19_3, true),
|
||||
map(0x1A, MINECRAFT_1_19_4, true),
|
||||
map(0x1B, MINECRAFT_1_20_2, true),
|
||||
map(0x1D, MINECRAFT_1_20_5, true));
|
||||
map(0x1D, MINECRAFT_1_20_5, true),
|
||||
map(0x1C, MINECRAFT_1_21_5, true),
|
||||
map(0x20, MINECRAFT_1_21_9, true));
|
||||
// minecraft:player_position
|
||||
clientbound.register(SetPlayerPositionRotationPacket.class, SetPlayerPositionRotationPacket::new,
|
||||
map(0x08, MINECRAFT_1_7_2, true),
|
||||
map(0x2E, MINECRAFT_1_9, true),
|
||||
@@ -154,7 +164,10 @@ public enum FallbackPacketRegistry {
|
||||
map(0x3C, MINECRAFT_1_19_4, true),
|
||||
map(0x3E, MINECRAFT_1_20_2, true),
|
||||
map(0x40, MINECRAFT_1_20_5, true),
|
||||
map(0x42, MINECRAFT_1_21_2, true));
|
||||
map(0x42, MINECRAFT_1_21_2, true),
|
||||
map(0x41, MINECRAFT_1_21_5, true),
|
||||
map(0x46, MINECRAFT_1_21_9, true));
|
||||
// minecraft:player_abilities
|
||||
clientbound.register(PlayerAbilitiesPacket.class, PlayerAbilitiesPacket::new,
|
||||
map(0x39, MINECRAFT_1_7_2, true),
|
||||
map(0x2B, MINECRAFT_1_9, true),
|
||||
@@ -171,7 +184,10 @@ public enum FallbackPacketRegistry {
|
||||
map(0x34, MINECRAFT_1_19_4, true),
|
||||
map(0x36, MINECRAFT_1_20_2, true),
|
||||
map(0x38, MINECRAFT_1_20_5, true),
|
||||
map(0x3A, MINECRAFT_1_21_2, true));
|
||||
map(0x3A, MINECRAFT_1_21_2, true),
|
||||
map(0x39, MINECRAFT_1_21_5, true),
|
||||
map(0x3E, MINECRAFT_1_21_9, true));
|
||||
// minecraft:level_chunk_with_light
|
||||
clientbound.register(ChunkDataPacket.class, ChunkDataPacket::new,
|
||||
map(0x21, MINECRAFT_1_7_2, true),
|
||||
map(0x20, MINECRAFT_1_9, true),
|
||||
@@ -187,7 +203,10 @@ public enum FallbackPacketRegistry {
|
||||
map(0x24, MINECRAFT_1_19_4, true),
|
||||
map(0x25, MINECRAFT_1_20_2, true),
|
||||
map(0x27, MINECRAFT_1_20_5, true),
|
||||
map(0x28, MINECRAFT_1_21_2, true));
|
||||
map(0x28, MINECRAFT_1_21_2, true),
|
||||
map(0x27, MINECRAFT_1_21_5, true),
|
||||
map(0x2C, MINECRAFT_1_21_9, true));
|
||||
// minecraft:remove_entities
|
||||
clientbound.register(RemoveEntitiesPacket.class, RemoveEntitiesPacket::new,
|
||||
map(0x13, MINECRAFT_1_7_2, true),
|
||||
map(0x30, MINECRAFT_1_9, true),
|
||||
@@ -206,7 +225,10 @@ public enum FallbackPacketRegistry {
|
||||
map(0x3E, MINECRAFT_1_19_4, true),
|
||||
map(0x40, MINECRAFT_1_20_2, true),
|
||||
map(0x42, MINECRAFT_1_20_5, true),
|
||||
map(0x47, MINECRAFT_1_21_2, true));
|
||||
map(0x47, MINECRAFT_1_21_2, true),
|
||||
map(0x46, MINECRAFT_1_21_5, true),
|
||||
map(0x4B, MINECRAFT_1_21_9, true));
|
||||
// minecraft:set_passengers
|
||||
clientbound.register(SetPassengersPacket.class, SetPassengersPacket::new,
|
||||
map(0x1B, MINECRAFT_1_7_2, true),
|
||||
map(0x40, MINECRAFT_1_9, true),
|
||||
@@ -222,26 +244,15 @@ public enum FallbackPacketRegistry {
|
||||
map(0x5B, MINECRAFT_1_20_2, true),
|
||||
map(0x5D, MINECRAFT_1_20_3, 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),
|
||||
map(0x2C, MINECRAFT_1_14, true),
|
||||
map(0x2D, MINECRAFT_1_15, true),
|
||||
map(0x2C, MINECRAFT_1_16, true),
|
||||
map(0x2B, MINECRAFT_1_16_2, true),
|
||||
map(0x2C, MINECRAFT_1_17, true),
|
||||
map(0x29, MINECRAFT_1_19, true),
|
||||
map(0x2B, MINECRAFT_1_19_1, true),
|
||||
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(0x33, MINECRAFT_1_21_2, true));
|
||||
map(0x65, MINECRAFT_1_21_2, true),
|
||||
map(0x64, MINECRAFT_1_21_5, true),
|
||||
map(0x69, MINECRAFT_1_21_9, true));
|
||||
// minecraft:add_entity
|
||||
clientbound.register(SpawnEntityPacket.class, SpawnEntityPacket::new,
|
||||
map(0x0E, MINECRAFT_1_7_2, true),
|
||||
map(0x00, MINECRAFT_1_9, true),
|
||||
map(0x01, MINECRAFT_1_19_4, true));
|
||||
// minecraft:section_blocks_update
|
||||
clientbound.register(UpdateSectionBlocksPacket.class, UpdateSectionBlocksPacket::new,
|
||||
map(0x22, MINECRAFT_1_7_2, true),
|
||||
map(0x10, MINECRAFT_1_9, true),
|
||||
@@ -257,7 +268,10 @@ public enum FallbackPacketRegistry {
|
||||
map(0x45, MINECRAFT_1_20_2, true),
|
||||
map(0x47, MINECRAFT_1_20_3, true),
|
||||
map(0x49, MINECRAFT_1_20_5, true),
|
||||
map(0x4E, MINECRAFT_1_21_2, true));
|
||||
map(0x4E, MINECRAFT_1_21_2, true),
|
||||
map(0x4D, MINECRAFT_1_21_5, true),
|
||||
map(0x52, MINECRAFT_1_21_9, true));
|
||||
// minecraft:ping
|
||||
clientbound.register(TransactionPacket.class, TransactionPacket::new,
|
||||
map(0x32, MINECRAFT_1_7_2, true),
|
||||
map(0x11, MINECRAFT_1_9, true),
|
||||
@@ -272,7 +286,10 @@ public enum FallbackPacketRegistry {
|
||||
map(0x32, MINECRAFT_1_19_4, true),
|
||||
map(0x33, MINECRAFT_1_20_2, true),
|
||||
map(0x35, MINECRAFT_1_20_5, true),
|
||||
map(0x37, MINECRAFT_1_21_2, true));
|
||||
map(0x37, MINECRAFT_1_21_2, true),
|
||||
map(0x36, MINECRAFT_1_21_5, true),
|
||||
map(0x3B, MINECRAFT_1_21_9, true));
|
||||
// minecraft:set_default_spawn_position
|
||||
clientbound.register(SetDefaultSpawnPositionPacket.class, SetDefaultSpawnPositionPacket::new,
|
||||
map(0x05, MINECRAFT_1_7_2, true),
|
||||
map(0x43, MINECRAFT_1_9, true),
|
||||
@@ -290,7 +307,10 @@ public enum FallbackPacketRegistry {
|
||||
map(0x52, MINECRAFT_1_20_2, true),
|
||||
map(0x54, MINECRAFT_1_20_3, true),
|
||||
map(0x56, MINECRAFT_1_20_5, true),
|
||||
map(0x5B, MINECRAFT_1_21_2, true));
|
||||
map(0x5B, MINECRAFT_1_21_2, true),
|
||||
map(0x5A, MINECRAFT_1_21_5, true),
|
||||
map(0x5F, MINECRAFT_1_21_9, true));
|
||||
// minecraft:map_item_data
|
||||
clientbound.register(MapDataPacket.class, MapDataPacket::new,
|
||||
map(0x34, MINECRAFT_1_7_2, true),
|
||||
map(0x24, MINECRAFT_1_9, true),
|
||||
@@ -305,7 +325,10 @@ public enum FallbackPacketRegistry {
|
||||
map(0x29, MINECRAFT_1_19_4, true),
|
||||
map(0x2A, MINECRAFT_1_20_2, true),
|
||||
map(0x2C, MINECRAFT_1_20_5, true),
|
||||
map(0x2D, MINECRAFT_1_21_2, true));
|
||||
map(0x2D, MINECRAFT_1_21_2, true),
|
||||
map(0x2C, MINECRAFT_1_21_5, true),
|
||||
map(0x31, MINECRAFT_1_21_9, true));
|
||||
// minecraft:container_set_slot
|
||||
clientbound.register(SetContainerSlotPacket.class, SetContainerSlotPacket::new,
|
||||
map(0x2F, MINECRAFT_1_7_2, true),
|
||||
map(0x16, MINECRAFT_1_9, true),
|
||||
@@ -318,7 +341,9 @@ public enum FallbackPacketRegistry {
|
||||
map(0x13, MINECRAFT_1_19, true),
|
||||
map(0x12, MINECRAFT_1_19_3, true),
|
||||
map(0x14, MINECRAFT_1_19_4, true),
|
||||
map(0x15, MINECRAFT_1_20_2, true));
|
||||
map(0x15, MINECRAFT_1_20_2, true),
|
||||
map(0x14, MINECRAFT_1_21_5, true));
|
||||
// minecraft:set_experience
|
||||
clientbound.register(SetExperiencePacket.class, SetExperiencePacket::new,
|
||||
map(0x1F, MINECRAFT_1_7_2, true),
|
||||
map(0x3D, MINECRAFT_1_9, true),
|
||||
@@ -334,7 +359,10 @@ public enum FallbackPacketRegistry {
|
||||
map(0x58, MINECRAFT_1_20_2, true),
|
||||
map(0x5A, MINECRAFT_1_20_3, true),
|
||||
map(0x5C, MINECRAFT_1_20_5, true),
|
||||
map(0x61, MINECRAFT_1_21_2, true));
|
||||
map(0x61, MINECRAFT_1_21_2, true),
|
||||
map(0x60, MINECRAFT_1_21_5, true),
|
||||
map(0x65, MINECRAFT_1_21_9, true));
|
||||
// minecraft:system_chat
|
||||
clientbound.register(SystemChatPacket.class, SystemChatPacket::new,
|
||||
map(0x02, MINECRAFT_1_7_2, true),
|
||||
map(0x0F, MINECRAFT_1_9, true),
|
||||
@@ -349,14 +377,22 @@ public enum FallbackPacketRegistry {
|
||||
map(0x67, MINECRAFT_1_20_2, true),
|
||||
map(0x69, MINECRAFT_1_20_3, true),
|
||||
map(0x6C, MINECRAFT_1_20_5, true),
|
||||
map(0x73, MINECRAFT_1_21_2, true));
|
||||
map(0x73, MINECRAFT_1_21_2, true),
|
||||
map(0x72, MINECRAFT_1_21_5, true),
|
||||
map(0x77, MINECRAFT_1_21_9, true));
|
||||
// minecraft:game_event
|
||||
clientbound.register(GameEventPacket.class, GameEventPacket::new,
|
||||
map(0x20, MINECRAFT_1_20_3, true),
|
||||
map(0x22, MINECRAFT_1_20_5, true),
|
||||
map(0x23, MINECRAFT_1_21_2, true));
|
||||
map(0x23, MINECRAFT_1_21_2, true),
|
||||
map(0x22, MINECRAFT_1_21_5, true),
|
||||
map(0x26, MINECRAFT_1_21_9, true));
|
||||
// minecraft:transfer
|
||||
clientbound.register(TransferPacket.class, TransferPacket::new,
|
||||
map(0x73, MINECRAFT_1_20_5, true),
|
||||
map(0x7A, MINECRAFT_1_21_2, true));
|
||||
map(0x7A, MINECRAFT_1_21_2, true),
|
||||
map(0x7F, MINECRAFT_1_21_9, true));
|
||||
// minecraft:set_held_slot
|
||||
clientbound.register(SetHeldItemPacket.class, SetHeldItemPacket::new,
|
||||
map(0x09, MINECRAFT_1_7_2, true),
|
||||
map(0x37, MINECRAFT_1_9, true),
|
||||
@@ -374,7 +410,10 @@ public enum FallbackPacketRegistry {
|
||||
map(0x4F, MINECRAFT_1_20_2, true),
|
||||
map(0x51, MINECRAFT_1_20_3, true),
|
||||
map(0x53, MINECRAFT_1_20_5, true),
|
||||
map(0x63, MINECRAFT_1_21_2, true));
|
||||
map(0x63, MINECRAFT_1_21_2, true),
|
||||
map(0x62, MINECRAFT_1_21_5, true),
|
||||
map(0x67, MINECRAFT_1_21_9, true));
|
||||
// minecraft:animate
|
||||
clientbound.register(EntityAnimationPacket.class, EntityAnimationPacket::new,
|
||||
map(0x0B, MINECRAFT_1_7_2, true),
|
||||
map(0x06, MINECRAFT_1_9, true),
|
||||
@@ -382,7 +421,9 @@ public enum FallbackPacketRegistry {
|
||||
map(0x06, MINECRAFT_1_17, true),
|
||||
map(0x03, MINECRAFT_1_19, true),
|
||||
map(0x04, MINECRAFT_1_19_4, true),
|
||||
map(0x03, MINECRAFT_1_20_2, true));
|
||||
map(0x03, MINECRAFT_1_20_2, true),
|
||||
map(0x02, MINECRAFT_1_21_5, true));
|
||||
// minecraft:set_time
|
||||
clientbound.register(UpdateTimePacket.class, UpdateTimePacket::new,
|
||||
map(0x03, MINECRAFT_1_7_2, true),
|
||||
map(0x44, MINECRAFT_1_9, true),
|
||||
@@ -400,10 +441,15 @@ public enum FallbackPacketRegistry {
|
||||
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));
|
||||
map(0x6B, MINECRAFT_1_21_2, true),
|
||||
map(0x6A, MINECRAFT_1_21_5, true),
|
||||
map(0x6F, MINECRAFT_1_21_9, true));
|
||||
|
||||
// minecraft:client_tick_end
|
||||
serverbound.register(ClientTickEndPacket.class, ClientTickEndPacket::new,
|
||||
map(0x0B, MINECRAFT_1_21_2, false));
|
||||
map(0x0B, MINECRAFT_1_21_2, false),
|
||||
map(0x0C, MINECRAFT_1_21_6, false));
|
||||
// minecraft:set_carried_item
|
||||
serverbound.register(SetHeldItemPacket.class, SetHeldItemPacket::new,
|
||||
map(0x09, MINECRAFT_1_7_2, false),
|
||||
map(0x17, MINECRAFT_1_9, false),
|
||||
@@ -418,7 +464,9 @@ public enum FallbackPacketRegistry {
|
||||
map(0x2C, MINECRAFT_1_20_3, false),
|
||||
map(0x2F, MINECRAFT_1_20_5, false),
|
||||
map(0x31, MINECRAFT_1_21_2, false),
|
||||
map(0x33, MINECRAFT_1_21_4, false));
|
||||
map(0x33, MINECRAFT_1_21_4, false),
|
||||
map(0x34, MINECRAFT_1_21_6, false));
|
||||
// minecraft:paddle_boat
|
||||
serverbound.register(PaddleBoatPacket.class, PaddleBoatPacket::new,
|
||||
map(0x11, MINECRAFT_1_9, false),
|
||||
map(0x12, MINECRAFT_1_12, false),
|
||||
@@ -434,7 +482,9 @@ public enum FallbackPacketRegistry {
|
||||
map(0x1B, MINECRAFT_1_20_2, false),
|
||||
map(0x1C, MINECRAFT_1_20_3, false),
|
||||
map(0x1F, MINECRAFT_1_20_5, false),
|
||||
map(0x21, MINECRAFT_1_21_2, false));
|
||||
map(0x21, MINECRAFT_1_21_2, false),
|
||||
map(0x22, MINECRAFT_1_21_6, false));
|
||||
// minecraft:player_input
|
||||
serverbound.register(PlayerInputPacket.class, PlayerInputPacket::new,
|
||||
map(0x0C, MINECRAFT_1_7_2, false),
|
||||
map(0x15, MINECRAFT_1_9, false),
|
||||
@@ -451,7 +501,9 @@ public enum FallbackPacketRegistry {
|
||||
map(0x23, MINECRAFT_1_20_3, false),
|
||||
map(0x26, MINECRAFT_1_20_5, false),
|
||||
map(0x28, MINECRAFT_1_21_2, false),
|
||||
map(0x29, MINECRAFT_1_21_4, false));
|
||||
map(0x29, MINECRAFT_1_21_4, false),
|
||||
map(0x2A, MINECRAFT_1_21_6, false));
|
||||
// minecraft:move_vehicle
|
||||
serverbound.register(VehicleMovePacket.class, VehicleMovePacket::new,
|
||||
map(0x10, MINECRAFT_1_9, false),
|
||||
map(0x11, MINECRAFT_1_12, false),
|
||||
@@ -467,7 +519,9 @@ public enum FallbackPacketRegistry {
|
||||
map(0x1A, MINECRAFT_1_20_2, false),
|
||||
map(0x1B, MINECRAFT_1_20_3, false),
|
||||
map(0x1E, MINECRAFT_1_20_5, false),
|
||||
map(0x20, MINECRAFT_1_21_2, false));
|
||||
map(0x20, MINECRAFT_1_21_2, false),
|
||||
map(0x21, MINECRAFT_1_21_6, false));
|
||||
// minecraft:chat
|
||||
serverbound.register(SystemChatPacket.class, SystemChatPacket::new,
|
||||
map(0x01, MINECRAFT_1_7_2, false),
|
||||
map(0x02, MINECRAFT_1_9, false),
|
||||
@@ -478,7 +532,9 @@ public enum FallbackPacketRegistry {
|
||||
map(0x05, MINECRAFT_1_19_1, false),
|
||||
map(0x05, MINECRAFT_1_19_3, false),
|
||||
map(0x06, MINECRAFT_1_20_5, false),
|
||||
map(0x07, MINECRAFT_1_21_2, false));
|
||||
map(0x07, MINECRAFT_1_21_2, false),
|
||||
map(0x08, MINECRAFT_1_21_6, false));
|
||||
// minecraft:keep_alive
|
||||
serverbound.register(KeepAlivePacket.class, KeepAlivePacket::new,
|
||||
map(0x00, MINECRAFT_1_7_2, false),
|
||||
map(0x0B, MINECRAFT_1_9, false),
|
||||
@@ -495,7 +551,9 @@ public enum FallbackPacketRegistry {
|
||||
map(0x14, MINECRAFT_1_20_2, false),
|
||||
map(0x15, MINECRAFT_1_20_3, false),
|
||||
map(0x18, MINECRAFT_1_20_5, false),
|
||||
map(0x1A, MINECRAFT_1_21_2, false));
|
||||
map(0x1A, MINECRAFT_1_21_2, false),
|
||||
map(0x1B, MINECRAFT_1_21_6, false));
|
||||
// minecraft:client_information
|
||||
serverbound.register(ClientInformationPacket.class, ClientInformationPacket::new,
|
||||
map(0x15, MINECRAFT_1_7_2, false),
|
||||
map(0x04, MINECRAFT_1_9, false),
|
||||
@@ -508,7 +566,9 @@ public enum FallbackPacketRegistry {
|
||||
map(0x08, MINECRAFT_1_19_4, false),
|
||||
map(0x09, MINECRAFT_1_20_2, false),
|
||||
map(0x0A, MINECRAFT_1_20_5, false),
|
||||
map(0x0C, MINECRAFT_1_21_2, false));
|
||||
map(0x0C, MINECRAFT_1_21_2, false),
|
||||
map(0x0D, MINECRAFT_1_21_6, false));
|
||||
// minecraft:custom_payload
|
||||
serverbound.register(PluginMessagePacket.class, PluginMessagePacket::new,
|
||||
map(0x17, MINECRAFT_1_7_2, false),
|
||||
map(0x09, MINECRAFT_1_9, false),
|
||||
@@ -523,7 +583,9 @@ public enum FallbackPacketRegistry {
|
||||
map(0x0D, MINECRAFT_1_19_4, false),
|
||||
map(0x0F, MINECRAFT_1_20_2, false),
|
||||
map(0x12, MINECRAFT_1_21, false),
|
||||
map(0x14, MINECRAFT_1_21_2, false));
|
||||
map(0x14, MINECRAFT_1_21_2, false),
|
||||
map(0x15, MINECRAFT_1_21_6, false));
|
||||
// minecraft:move_player_status_only
|
||||
serverbound.register(SetPlayerOnGround.class, SetPlayerOnGround::new,
|
||||
map(0x03, MINECRAFT_1_7_2, false),
|
||||
map(0x0F, MINECRAFT_1_9, false),
|
||||
@@ -540,7 +602,9 @@ public enum FallbackPacketRegistry {
|
||||
map(0x19, MINECRAFT_1_20_2, false),
|
||||
map(0x1A, MINECRAFT_1_20_3, false),
|
||||
map(0x1D, MINECRAFT_1_20_5, false),
|
||||
map(0x1F, MINECRAFT_1_21_2, false));
|
||||
map(0x1F, MINECRAFT_1_21_2, false),
|
||||
map(0x20, MINECRAFT_1_21_6, false));
|
||||
// minecraft:move_player_pos
|
||||
serverbound.register(SetPlayerPositionPacket.class, SetPlayerPositionPacket::new,
|
||||
map(0x04, MINECRAFT_1_7_2, false),
|
||||
map(0x0C, MINECRAFT_1_9, false),
|
||||
@@ -557,7 +621,9 @@ public enum FallbackPacketRegistry {
|
||||
map(0x16, MINECRAFT_1_20_2, false),
|
||||
map(0x17, MINECRAFT_1_20_3, false),
|
||||
map(0x1A, MINECRAFT_1_20_5, false),
|
||||
map(0x1C, MINECRAFT_1_21_2, false));
|
||||
map(0x1C, MINECRAFT_1_21_2, false),
|
||||
map(0x1D, MINECRAFT_1_21_6, false));
|
||||
// minecraft:move_player_rot
|
||||
serverbound.register(SetPlayerRotationPacket.class, SetPlayerRotationPacket::new,
|
||||
map(0x05, MINECRAFT_1_7_2, false),
|
||||
map(0x0E, MINECRAFT_1_9, false),
|
||||
@@ -574,7 +640,9 @@ public enum FallbackPacketRegistry {
|
||||
map(0x18, MINECRAFT_1_20_2, false),
|
||||
map(0x19, MINECRAFT_1_20_3, false),
|
||||
map(0x1C, MINECRAFT_1_20_5, false),
|
||||
map(0x1E, MINECRAFT_1_21_2, false));
|
||||
map(0x1E, MINECRAFT_1_21_2, false),
|
||||
map(0x1F, MINECRAFT_1_21_6, false));
|
||||
// minecraft:move_player_pos_rot
|
||||
serverbound.register(SetPlayerPositionRotationPacket.class, SetPlayerPositionRotationPacket::new,
|
||||
map(0x06, MINECRAFT_1_7_2, false),
|
||||
map(0x0D, MINECRAFT_1_9, false),
|
||||
@@ -591,9 +659,12 @@ public enum FallbackPacketRegistry {
|
||||
map(0x17, MINECRAFT_1_20_2, false),
|
||||
map(0x18, MINECRAFT_1_20_3, false),
|
||||
map(0x1B, MINECRAFT_1_20_5, false),
|
||||
map(0x1D, MINECRAFT_1_21_2, false));
|
||||
map(0x1D, MINECRAFT_1_21_2, false),
|
||||
map(0x1E, MINECRAFT_1_21_6, false));
|
||||
// minecraft:accept_teleportation
|
||||
serverbound.register(ConfirmTeleportationPacket.class, ConfirmTeleportationPacket::new,
|
||||
map(0x00, MINECRAFT_1_9, false));
|
||||
// minecraft:pong
|
||||
serverbound.register(TransactionPacket.class, TransactionPacket::new,
|
||||
map(0x0F, MINECRAFT_1_7_2, false),
|
||||
map(0x05, MINECRAFT_1_9, false),
|
||||
@@ -610,7 +681,9 @@ public enum FallbackPacketRegistry {
|
||||
map(0x24, MINECRAFT_1_20_3, false),
|
||||
map(0x27, MINECRAFT_1_20_5, false),
|
||||
map(0x29, MINECRAFT_1_21_2, false),
|
||||
map(0x2B, MINECRAFT_1_21_4, false));
|
||||
map(0x2B, MINECRAFT_1_21_4, false),
|
||||
map(0x2C, MINECRAFT_1_21_6, false));
|
||||
// minecraft:swing
|
||||
serverbound.register(AnimationPacket.class, AnimationPacket::new,
|
||||
map(0x0A, MINECRAFT_1_7_2, false),
|
||||
map(0x1A, MINECRAFT_1_9, false),
|
||||
@@ -625,7 +698,9 @@ public enum FallbackPacketRegistry {
|
||||
map(0x33, MINECRAFT_1_20_3, false),
|
||||
map(0x36, MINECRAFT_1_20_5, false),
|
||||
map(0x38, MINECRAFT_1_21_2, false),
|
||||
map(0x3A, MINECRAFT_1_21_4, false));
|
||||
map(0x3A, MINECRAFT_1_21_4, false),
|
||||
map(0x3B, MINECRAFT_1_21_5, false),
|
||||
map(0x3C, MINECRAFT_1_21_6, false));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -637,8 +712,8 @@ public enum FallbackPacketRegistry {
|
||||
CLIENTBOUND
|
||||
}
|
||||
|
||||
public FallbackPacketRegistry.ProtocolRegistry getProtocolRegistry(final Direction direction,
|
||||
final ProtocolVersion version) {
|
||||
public SonarPacketRegistry.ProtocolRegistry getProtocolRegistry(final Direction direction,
|
||||
final ProtocolVersion version) {
|
||||
return (direction == Direction.SERVERBOUND ? serverbound : clientbound).getProtocolRegistry(version);
|
||||
}
|
||||
|
||||
@@ -661,9 +736,9 @@ public enum FallbackPacketRegistry {
|
||||
return registry;
|
||||
}
|
||||
|
||||
private <T extends FallbackPacket> void register(final @NotNull Class<T> clazz,
|
||||
final @NotNull Supplier<T> supplier,
|
||||
final PacketMapping @NotNull ... mappings) {
|
||||
private <T extends SonarPacket> void register(final @NotNull Class<T> clazz,
|
||||
final @NotNull Supplier<T> supplier,
|
||||
final PacketMapping @NotNull ... mappings) {
|
||||
if (mappings.length == 0) {
|
||||
throw new IllegalArgumentException("At least one mapping must be provided.");
|
||||
}
|
||||
@@ -708,17 +783,17 @@ public enum FallbackPacketRegistry {
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public static class ProtocolRegistry {
|
||||
private final ProtocolVersion protocolVersion;
|
||||
private final IntObjectMap<Supplier<? extends FallbackPacket>> packetIdToSupplier =
|
||||
private final IntObjectMap<Supplier<? extends SonarPacket>> packetIdToSupplier =
|
||||
new IntObjectHashMap<>(16, 0.5f);
|
||||
private final Map<Class<? extends FallbackPacket>, Integer> packetClassToId =
|
||||
private final Map<Class<? extends SonarPacket>, Integer> packetClassToId =
|
||||
new HashMap<>(16, 0.5f);
|
||||
|
||||
public FallbackPacket createPacket(final int packetId) {
|
||||
public SonarPacket createPacket(final int packetId) {
|
||||
final var supplier = packetIdToSupplier.get(packetId);
|
||||
return supplier == null ? null : supplier.get();
|
||||
}
|
||||
|
||||
public int getPacketId(final @NotNull Class<? extends FallbackPacket> clazz) {
|
||||
public int getPacketId(final @NotNull Class<? extends SonarPacket> clazz) {
|
||||
final int packetId = packetClassToId.getOrDefault(clazz, -1);
|
||||
if (packetId == -1) {
|
||||
throw new IllegalArgumentException("Could not find packet ID for " + clazz.getSimpleName());
|
||||
+6
-6
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol;
|
||||
package xyz.jonesdev.sonar.common.protocol;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
@@ -25,20 +25,20 @@ 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 xyz.jonesdev.sonar.api.antibot.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.ID_TO_PROTOCOL_CONSTANT;
|
||||
import static xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion.ID_TO_PROTOCOL_CONSTANT;
|
||||
|
||||
// https://github.com/Nan1t/NanoLimbo/blob/main/src/main/java/ua/nanit/limbo/protocol/PacketSnapshot.java
|
||||
public final class FallbackPacketSnapshot implements FallbackPacket {
|
||||
public final class SonarPacketSnapshot implements SonarPacket {
|
||||
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;
|
||||
private final Class<? extends SonarPacket> originalPacketClass;
|
||||
|
||||
public FallbackPacketSnapshot(final @NotNull FallbackPacket originalPacket) {
|
||||
public SonarPacketSnapshot(final @NotNull SonarPacket originalPacket) {
|
||||
this.originalPacketClass = originalPacket.getClass();
|
||||
|
||||
final IntObjectMap<Integer> hashedData = new IntObjectHashMap<>(ID_TO_PROTOCOL_CONSTANT.size());
|
||||
+52
-11
@@ -15,15 +15,15 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.block;
|
||||
package xyz.jonesdev.sonar.common.protocol.block;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion.*;
|
||||
|
||||
// TODO: load mappings from a separate file
|
||||
@Getter
|
||||
@@ -34,7 +34,6 @@ public enum BlockType {
|
||||
// - https://github.com/PrismarineJS/minecraft-data/blob/master/data/pc/
|
||||
// - https://pokechu22.github.io/Burger/
|
||||
// - https://github.com/ViaVersion/Mappings/tree/main/mappings
|
||||
//STONE(protocolVersion -> 1, 1),
|
||||
ENCHANTMENT_TABLE(protocolVersion -> {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
|
||||
return 116;
|
||||
@@ -72,7 +71,13 @@ public enum BlockType {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_2)) {
|
||||
return 7619;
|
||||
}
|
||||
return 8163;
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_4)) {
|
||||
return 8163;
|
||||
}
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_7)) {
|
||||
return 8173;
|
||||
}
|
||||
return 9250;
|
||||
}, protocolVersion -> (double) 0.75f),
|
||||
TRAPDOOR(protocolVersion -> {
|
||||
// We have to use wooden trapdoors for 1.7 since 1.7 doesn't have iron trapdoors
|
||||
@@ -118,7 +123,13 @@ public enum BlockType {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_2)) {
|
||||
return 10749;
|
||||
}
|
||||
return 11293;
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_4)) {
|
||||
return 11293;
|
||||
}
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_7)) {
|
||||
return 11303;
|
||||
}
|
||||
return 12380;
|
||||
}, protocolVersion -> 0.1875),
|
||||
END_PORTAL_FRAME(protocolVersion -> {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
|
||||
@@ -157,7 +168,13 @@ public enum BlockType {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_2)) {
|
||||
return 7644;
|
||||
}
|
||||
return 8185;
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_4)) {
|
||||
return 8185;
|
||||
}
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_7)) {
|
||||
return 8195;
|
||||
}
|
||||
return 9272;
|
||||
}, protocolVersion -> 0.8125),
|
||||
DAYLIGHT_SENSOR(protocolVersion -> {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
|
||||
@@ -199,7 +216,13 @@ public enum BlockType {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_2)) {
|
||||
return 9462;
|
||||
}
|
||||
return 10006;
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_4)) {
|
||||
return 10006;
|
||||
}
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_7)) {
|
||||
return 10016;
|
||||
}
|
||||
return 11093;
|
||||
}, protocolVersion -> 0.375),
|
||||
COBBLESTONE_WALL(protocolVersion -> {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
|
||||
@@ -238,7 +261,13 @@ public enum BlockType {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_2)) {
|
||||
return 8152;
|
||||
}
|
||||
return 8696;
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_4)) {
|
||||
return 8696;
|
||||
}
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_7)) {
|
||||
return 8706;
|
||||
}
|
||||
return 9783;
|
||||
}, protocolVersion -> 1.5),
|
||||
STONE_SLABS(protocolVersion -> {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
|
||||
@@ -280,7 +309,13 @@ public enum BlockType {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_2)) {
|
||||
return 11566;
|
||||
}
|
||||
return 12110;
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_4)) {
|
||||
return 12110;
|
||||
}
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_7)) {
|
||||
return 12120;
|
||||
}
|
||||
return 13197;
|
||||
}, protocolVersion -> 0.5),
|
||||
WHITE_CARPET(protocolVersion -> {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_12_2)) {
|
||||
@@ -322,7 +357,13 @@ public enum BlockType {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_2)) {
|
||||
return 11063;
|
||||
}
|
||||
return 11607;
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_4)) {
|
||||
return 11607;
|
||||
}
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_7)) {
|
||||
return 11617;
|
||||
}
|
||||
return 12694;
|
||||
}, protocolVersion -> protocolVersion.compareTo(MINECRAFT_1_8) < 0 ? 0 : 0.0625);
|
||||
|
||||
private final Function<ProtocolVersion, Integer> id;
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.block;
|
||||
package xyz.jonesdev.sonar.common.protocol.block;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
+5
-1
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.dimension;
|
||||
package xyz.jonesdev.sonar.common.protocol.dimension;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import net.kyori.adventure.nbt.BinaryTagIO;
|
||||
@@ -39,6 +39,8 @@ public final class DimensionRegistry {
|
||||
public final CompoundBinaryTag CODEC_1_21;
|
||||
public final CompoundBinaryTag CODEC_1_21_2;
|
||||
public final CompoundBinaryTag CODEC_1_21_4;
|
||||
public final CompoundBinaryTag CODEC_1_21_5;
|
||||
public final CompoundBinaryTag CODEC_1_21_11;
|
||||
|
||||
static {
|
||||
CODEC_1_16 = getCodec("codec_1_16.nbt");
|
||||
@@ -52,6 +54,8 @@ public final class DimensionRegistry {
|
||||
CODEC_1_21 = getCodec("codec_1_21.nbt");
|
||||
CODEC_1_21_2 = getCodec("codec_1_21_2.nbt");
|
||||
CODEC_1_21_4 = getCodec("codec_1_21_4.nbt");
|
||||
CODEC_1_21_5 = getCodec("codec_1_21_5.nbt");
|
||||
CODEC_1_21_11 = getCodec("codec_1_21_11.nbt");
|
||||
}
|
||||
|
||||
private @NotNull CompoundBinaryTag getCodec(final @NotNull String fileName) {
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.dimension;
|
||||
package xyz.jonesdev.sonar.common.protocol.dimension;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
+23
-5
@@ -15,15 +15,15 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.entity;
|
||||
package xyz.jonesdev.sonar.common.protocol.entity;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion.*;
|
||||
|
||||
// TODO: load mappings from a separate file
|
||||
@Getter
|
||||
@@ -58,7 +58,16 @@ public enum EntityType {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_2)) {
|
||||
return 85;
|
||||
}
|
||||
return 84;
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_5)) {
|
||||
return 84;
|
||||
}
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_7)) {
|
||||
return 85;
|
||||
}
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_9)) {
|
||||
return 87;
|
||||
}
|
||||
return 89;
|
||||
}),
|
||||
MINECART(protocolVersion -> {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_13_2)) {
|
||||
@@ -94,7 +103,16 @@ public enum EntityType {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_2)) {
|
||||
return 82;
|
||||
}
|
||||
return 81;
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_5)) {
|
||||
return 81;
|
||||
}
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_7)) {
|
||||
return 82;
|
||||
}
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_9)) {
|
||||
return 84;
|
||||
}
|
||||
return 85;
|
||||
});
|
||||
|
||||
private final Function<ProtocolVersion, Integer> id;
|
||||
+25
-5
@@ -15,15 +15,15 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.item;
|
||||
package xyz.jonesdev.sonar.common.protocol.item;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import static xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion.*;
|
||||
import static xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion.*;
|
||||
|
||||
// TODO: load mappings from a separate file
|
||||
@Getter
|
||||
@@ -73,8 +73,28 @@ public enum ItemType {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_2)) {
|
||||
return 1022;
|
||||
}
|
||||
return 1031;
|
||||
}, protocolVersion -> protocolVersion.greaterThanOrEquals(MINECRAFT_1_21_2) ? 36 : 26);
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_4)) {
|
||||
return 1031;
|
||||
}
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_5)) {
|
||||
return 1042;
|
||||
}
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_7)) {
|
||||
return 1059;
|
||||
}
|
||||
return 1104;
|
||||
}, protocolVersion -> {
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21)) {
|
||||
return 26;
|
||||
}
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_4)) {
|
||||
return 36;
|
||||
}
|
||||
if (protocolVersion.lessThanOrEquals(MINECRAFT_1_21_9)) {
|
||||
return 37;
|
||||
}
|
||||
return 44;
|
||||
});
|
||||
|
||||
private final Function<ProtocolVersion, Integer> id;
|
||||
private final Function<ProtocolVersion, Integer> components;
|
||||
+10
-10
@@ -15,21 +15,21 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.map;
|
||||
package xyz.jonesdev.sonar.common.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 xyz.jonesdev.sonar.api.antibot.SonarUser;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.protocol.packets.play.MapDataPacket;
|
||||
|
||||
@Getter
|
||||
public final class MapCaptchaInfo {
|
||||
private final String answer;
|
||||
|
||||
private final FallbackPacket[] legacy;
|
||||
private final FallbackPacket modern;
|
||||
private final SonarPacket[] legacy;
|
||||
private final SonarPacket modern;
|
||||
|
||||
public MapCaptchaInfo(final @NotNull String answer, final byte @NotNull [] buffer) {
|
||||
this.answer = answer;
|
||||
@@ -39,7 +39,7 @@ public final class MapCaptchaInfo {
|
||||
for (int i = 0; i < buffer.length; i++) {
|
||||
grid[i & Byte.MAX_VALUE][i >> 7] = buffer[i];
|
||||
}
|
||||
this.legacy = new FallbackPacket[grid.length];
|
||||
this.legacy = new SonarPacket[grid.length];
|
||||
for (int i = 0; i < grid.length; i++) {
|
||||
this.legacy[i] = new MapDataPacket(grid[i], i, 0, 0, true);
|
||||
}
|
||||
@@ -48,10 +48,10 @@ public final class MapCaptchaInfo {
|
||||
this.modern = new MapDataPacket(buffer, 0, 0, 0, true);
|
||||
}
|
||||
|
||||
public void delayedWrite(final @NotNull FallbackUser user) {
|
||||
public void delayedWrite(final @NotNull SonarUser user) {
|
||||
if (user.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||
// 1.7.2-1.7.10 needs separate packets for each axis
|
||||
for (final FallbackPacket packet : legacy) {
|
||||
for (final SonarPacket packet : legacy) {
|
||||
user.delayedWrite(packet);
|
||||
}
|
||||
return;
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.map;
|
||||
package xyz.jonesdev.sonar.common.protocol.map;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
+8
-4
@@ -15,13 +15,17 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.configuration;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.configuration;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
|
||||
public final class FinishConfigurationPacket implements FallbackPacket {
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class FinishConfigurationPacket implements SonarPacket {
|
||||
public static final FinishConfigurationPacket INSTANCE = new FinishConfigurationPacket();
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
+8
-8
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.configuration;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.configuration;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.*;
|
||||
@@ -23,9 +23,9 @@ import net.kyori.adventure.nbt.BinaryTag;
|
||||
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacketSnapshot;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacketSnapshot;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -34,7 +34,7 @@ import java.util.List;
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class RegistryDataPacket implements FallbackPacket {
|
||||
public final class RegistryDataPacket implements SonarPacket {
|
||||
private CompoundBinaryTag tag;
|
||||
private String type;
|
||||
private List<RegistryDataPacket.Bundle> bundles;
|
||||
@@ -66,8 +66,8 @@ public final class RegistryDataPacket implements FallbackPacket {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public static FallbackPacket @NotNull [] of(final @NotNull CompoundBinaryTag rootTag) {
|
||||
final FallbackPacket[] packets = new FallbackPacket[rootTag.size()];
|
||||
public static SonarPacket @NotNull [] of(final @NotNull CompoundBinaryTag rootTag) {
|
||||
final SonarPacket[] packets = new SonarPacket[rootTag.size()];
|
||||
int index = 0;
|
||||
for (final String type : rootTag.keySet()) {
|
||||
final ArrayList<RegistryDataPacket.Bundle> bundles = new ArrayList<>();
|
||||
@@ -76,7 +76,7 @@ public final class RegistryDataPacket implements FallbackPacket {
|
||||
// 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));
|
||||
packets[index++] = new SonarPacketSnapshot(new RegistryDataPacket(rootTag, type, bundles));
|
||||
}
|
||||
return packets;
|
||||
}
|
||||
+4
-4
@@ -15,18 +15,18 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.handshake;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.handshake;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString(of = {"protocolVersionId", "hostname", "port"})
|
||||
public final class HandshakePacket implements FallbackPacket {
|
||||
public final class HandshakePacket implements SonarPacket {
|
||||
private int protocolVersionId;
|
||||
private String hostname;
|
||||
private int port;
|
||||
+4
-4
@@ -15,13 +15,13 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.login;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.login;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
|
||||
public final class LoginAcknowledgedPacket implements FallbackPacket {
|
||||
public final class LoginAcknowledgedPacket implements SonarPacket {
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
+4
-4
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.login;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.login;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
@@ -25,8 +25,8 @@ import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
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.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
import xyz.jonesdev.sonar.common.util.exception.QuietDecoderException;
|
||||
|
||||
@@ -36,7 +36,7 @@ import java.util.UUID;
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class LoginStartPacket implements FallbackPacket {
|
||||
public final class LoginStartPacket implements SonarPacket {
|
||||
private String username;
|
||||
private @Nullable UUID uuid;
|
||||
|
||||
+4
-4
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.login;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.login;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -23,8 +23,8 @@ import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.util.FastUuidSansHyphens;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@@ -34,7 +34,7 @@ import java.util.UUID;
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class LoginSuccessPacket implements FallbackPacket {
|
||||
public final class LoginSuccessPacket implements SonarPacket {
|
||||
private UUID uuid;
|
||||
private String username;
|
||||
private boolean strictErrorHandling;
|
||||
+4
-4
@@ -15,13 +15,13 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.play;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.play;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.*;
|
||||
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.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@@ -29,7 +29,7 @@ import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString(of = {"entityId", "hand"})
|
||||
public final class AnimationPacket implements FallbackPacket {
|
||||
public final class AnimationPacket implements SonarPacket {
|
||||
private int entityId = -1;
|
||||
private int hand = MAIN_HAND;
|
||||
private LegacyAnimationType type = LegacyAnimationType.SWING_ARM;
|
||||
+29
-13
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.play;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.play;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -25,15 +25,15 @@ import lombok.ToString;
|
||||
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||
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 xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class ChunkDataPacket implements FallbackPacket {
|
||||
public final class ChunkDataPacket implements SonarPacket {
|
||||
private int sectionX, sectionZ;
|
||||
|
||||
@Override
|
||||
@@ -60,15 +60,26 @@ public final class ChunkDataPacket implements FallbackPacket {
|
||||
}
|
||||
|
||||
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.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_5)) {
|
||||
// In 1.21.5. They changed to List<EnumMap<Heightmap.Type, long[]>>
|
||||
ProtocolUtil.writeVarInt(byteBuf, 1); // List size
|
||||
ProtocolUtil.writeVarInt(byteBuf, 4); // Ordinal of MOTION_BLOCKING
|
||||
// Write long array
|
||||
ProtocolUtil.writeVarInt(byteBuf, 37);
|
||||
for (int i = 0; i < 37; i++) {
|
||||
byteBuf.writeLong(0);
|
||||
}
|
||||
} else { // Nbt for older version
|
||||
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();
|
||||
|
||||
ProtocolUtil.writeBinaryTag(byteBuf, protocolVersion, rootTag);
|
||||
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)) {
|
||||
@@ -95,7 +106,12 @@ public final class ChunkDataPacket implements FallbackPacket {
|
||||
} else if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_18)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, 0);
|
||||
} else {
|
||||
final byte[] sectionData = new byte[]{0, 0, 0, 0, 0, 0, 1, 0};
|
||||
// nonEmptyBlockCount. short occupies 2 bytes.
|
||||
// SingularPalette (byte) + id (varint) + long array with size (older)
|
||||
// Since 1.21.5. The length of the long array of PaletteStorage no longer depends on the VarInt in the packet
|
||||
// SingularPalette doesn't need to read any additional long array.
|
||||
// So we'll remove suffix 0 as array length here. The cost of writing a palette has been reduced from 3 to 2 bytes.
|
||||
final byte[] sectionData = protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_21_5) ? new byte[]{0, 0, 0, 0, 0, 0, 1, 0} : new byte[]{0, 0, 0, 0, 0, 1};
|
||||
int count = 24;
|
||||
ProtocolUtil.writeVarInt(byteBuf, sectionData.length * count);
|
||||
|
||||
+4
-4
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.play;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.play;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -23,15 +23,15 @@ import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class ClientInformationPacket implements FallbackPacket {
|
||||
public final class ClientInformationPacket implements SonarPacket {
|
||||
private String locale;
|
||||
private byte viewDistance;
|
||||
private int chatVisibility;
|
||||
+4
-4
@@ -15,13 +15,13 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.play;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.play;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
|
||||
public final class ClientTickEndPacket implements FallbackPacket {
|
||||
public final class ClientTickEndPacket implements SonarPacket {
|
||||
|
||||
@Override
|
||||
public void encode(final ByteBuf byteBuf, final ProtocolVersion protocolVersion) {
|
||||
+4
-4
@@ -15,22 +15,22 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.play;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.play;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class ConfirmTeleportationPacket implements FallbackPacket {
|
||||
public final class ConfirmTeleportationPacket implements SonarPacket {
|
||||
private int teleportId;
|
||||
|
||||
@Override
|
||||
+4
-4
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.play;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.play;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -23,14 +23,14 @@ import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.util.ComponentHolder;
|
||||
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class DisconnectPacket implements FallbackPacket {
|
||||
public final class DisconnectPacket implements SonarPacket {
|
||||
private @NotNull ComponentHolder componentHolder;
|
||||
private boolean duringLogin;
|
||||
|
||||
+4
-4
@@ -15,22 +15,22 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.play;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.play;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class EntityAnimationPacket implements FallbackPacket {
|
||||
public final class EntityAnimationPacket implements SonarPacket {
|
||||
private int entityId;
|
||||
private Type type;
|
||||
|
||||
+4
-4
@@ -15,20 +15,20 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.play;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.play;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
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 xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class GameEventPacket implements FallbackPacket {
|
||||
public final class GameEventPacket implements SonarPacket {
|
||||
private int type;
|
||||
private float value;
|
||||
|
||||
+6
-6
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.play;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.play;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -25,10 +25,10 @@ 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.DimensionRegistry;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.dimension.DimensionType;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.protocol.dimension.DimensionRegistry;
|
||||
import xyz.jonesdev.sonar.common.protocol.dimension.DimensionType;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
import java.util.Objects;
|
||||
@@ -36,7 +36,7 @@ import java.util.Objects;
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class JoinGamePacket implements FallbackPacket {
|
||||
public final class JoinGamePacket implements SonarPacket {
|
||||
private int entityId;
|
||||
private int gamemode;
|
||||
private int previousGamemode;
|
||||
+4
-4
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.play;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.play;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -23,15 +23,15 @@ import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class KeepAlivePacket implements FallbackPacket {
|
||||
public final class KeepAlivePacket implements SonarPacket {
|
||||
private long id;
|
||||
|
||||
@Override
|
||||
+4
-4
@@ -15,21 +15,21 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.play;
|
||||
package xyz.jonesdev.sonar.common.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;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class MapDataPacket implements FallbackPacket {
|
||||
public final class MapDataPacket implements SonarPacket {
|
||||
private byte[] buffer;
|
||||
private int x, y;
|
||||
private int scaling;
|
||||
+4
-4
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.play;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.play;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -23,14 +23,14 @@ import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class PaddleBoatPacket implements FallbackPacket {
|
||||
public final class PaddleBoatPacket implements SonarPacket {
|
||||
private boolean leftPaddle, rightPaddle;
|
||||
|
||||
@Override
|
||||
+4
-4
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.play;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.play;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -23,14 +23,14 @@ import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class PlayerAbilitiesPacket implements FallbackPacket {
|
||||
public final class PlayerAbilitiesPacket implements SonarPacket {
|
||||
private int encodedFlags;
|
||||
private float flySpeed, walkSpeed;
|
||||
|
||||
+4
-4
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.play;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.play;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -23,14 +23,14 @@ import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class PlayerInputPacket implements FallbackPacket {
|
||||
public final class PlayerInputPacket implements SonarPacket {
|
||||
private float sideways, forward;
|
||||
private boolean jump, sneak;
|
||||
|
||||
+5
-5
@@ -15,20 +15,20 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.play;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.play;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.Getter;
|
||||
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.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
import xyz.jonesdev.sonar.common.util.exception.QuietDecoderException;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
public final class PluginMessagePacket implements FallbackPacket {
|
||||
public final class PluginMessagePacket implements SonarPacket {
|
||||
private String channel;
|
||||
private byte[] data;
|
||||
|
||||
@@ -67,6 +67,6 @@ public final class PluginMessagePacket implements FallbackPacket {
|
||||
|
||||
@Override
|
||||
public int expectedMaxLength(final ProtocolVersion protocolVersion) {
|
||||
return 0xFFF; // strict size limit
|
||||
return Short.MAX_VALUE; // vanilla size limit
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.play;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.play;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -23,15 +23,15 @@ import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class RemoveEntitiesPacket implements FallbackPacket {
|
||||
public final class RemoveEntitiesPacket implements SonarPacket {
|
||||
private int entityId; // We only need one entityId
|
||||
|
||||
@Override
|
||||
+10
-10
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.play;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.play;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -24,16 +24,16 @@ import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
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.item.ItemType;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.protocol.item.ItemType;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class SetContainerSlotPacket implements FallbackPacket {
|
||||
public final class SetContainerSlotPacket implements SonarPacket {
|
||||
private int windowId, slot, count;
|
||||
private ItemType itemType;
|
||||
private CompoundBinaryTag compoundBinaryTag;
|
||||
@@ -60,10 +60,10 @@ public final class SetContainerSlotPacket implements FallbackPacket {
|
||||
ProtocolUtil.writeVarInt(byteBuf, count);
|
||||
}
|
||||
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_13_2)) {
|
||||
byteBuf.writeShort(itemType.getId().apply(protocolVersion));
|
||||
} else {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_13_2)) {
|
||||
ProtocolUtil.writeVarInt(byteBuf, itemType.getId().apply(protocolVersion));
|
||||
} else {
|
||||
byteBuf.writeShort(itemType.getId().apply(protocolVersion));
|
||||
}
|
||||
|
||||
if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_5)) {
|
||||
@@ -83,8 +83,8 @@ public final class SetContainerSlotPacket implements FallbackPacket {
|
||||
} else if (protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_5)) {
|
||||
ProtocolUtil.writeBinaryTag(byteBuf, protocolVersion, compoundBinaryTag);
|
||||
} else {
|
||||
ProtocolUtil.writeVarInt(byteBuf, 1); // component count to add
|
||||
ProtocolUtil.writeVarInt(byteBuf, 0); // component count to remove
|
||||
ProtocolUtil.writeVarInt(byteBuf, 1); // addedComponentCount
|
||||
ProtocolUtil.writeVarInt(byteBuf, 0); // removedComponentCount
|
||||
ProtocolUtil.writeVarInt(byteBuf, itemType.getComponents().apply(protocolVersion));
|
||||
ProtocolUtil.writeVarInt(byteBuf, 0); // data
|
||||
}
|
||||
+14
-4
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.play;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.play;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -23,14 +23,16 @@ import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class SetDefaultSpawnPositionPacket implements FallbackPacket {
|
||||
public final class SetDefaultSpawnPositionPacket implements SonarPacket {
|
||||
private String dimensionName;
|
||||
private int x, y, z;
|
||||
|
||||
@Override
|
||||
@@ -40,6 +42,10 @@ public final class SetDefaultSpawnPositionPacket implements FallbackPacket {
|
||||
byteBuf.writeInt(y);
|
||||
byteBuf.writeInt(z);
|
||||
} else {
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_9)) {
|
||||
ProtocolUtil.writeString(byteBuf, dimensionName);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -48,6 +54,10 @@ public final class SetDefaultSpawnPositionPacket implements FallbackPacket {
|
||||
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_17)) {
|
||||
byteBuf.writeFloat(0f);
|
||||
|
||||
if (protocolVersion.greaterThanOrEquals(ProtocolVersion.MINECRAFT_1_21_9)) {
|
||||
byteBuf.writeFloat(0f); // pitch
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -15,7 +15,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.jonesdev.sonar.common.fallback.protocol.packets.play;
|
||||
package xyz.jonesdev.sonar.common.protocol.packets.play;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.AllArgsConstructor;
|
||||
@@ -23,15 +23,15 @@ import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.jonesdev.sonar.api.fallback.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.fallback.protocol.FallbackPacket;
|
||||
import xyz.jonesdev.sonar.api.antibot.protocol.ProtocolVersion;
|
||||
import xyz.jonesdev.sonar.common.protocol.SonarPacket;
|
||||
import xyz.jonesdev.sonar.common.util.ProtocolUtil;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public final class SetExperiencePacket implements FallbackPacket {
|
||||
public final class SetExperiencePacket implements SonarPacket {
|
||||
private float experienceBar;
|
||||
private int level;
|
||||
private int totalExperience;
|
||||
Loaded 100 of 163 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user