Compare commits

...
9 Commits
Author SHA1 Message Date
Jones c12d77fb8d Release 2.0.37 (#346) 2024-07-04 16:24:03 +02:00
Michel Elkenwaat a9a23aaf2f Fix client brand reading (Revert https://github.com/jonesdevelopment/sonar/commit/e0cc9bdab3ee93a5c328f3015bd3564f31ed7e0c) 2024-07-04 16:19:11 +02:00
Jones 58d0c57ee8 Release 2.0.36 (#345) 2024-07-03 14:47:01 +02:00
Michel Elkenwaat 5546d2695d Add Indonesian translations (thanks to LourenT) 2024-07-02 14:19:43 +02:00
dependabot[bot] 64688ba06b Bump lombok from 1.18.32 to 1.18.34 (#343)
Bumps [org.projectlombok:lombok](https://github.com/projectlombok/lombok) from 1.18.32 to 1.18.34.
- [Changelog](https://github.com/projectlombok/lombok/blob/master/doc/changelog.markdown)
- [Commits](https://github.com/projectlombok/lombok/compare/v1.18.32...v1.18.34)

---
updated-dependencies:
- dependency-name: org.projectlombok:lombok
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-01 15:23:27 +02:00
dependabot[bot] e85036dd13 Bump io.github.goooler.shadow from 8.1.7 to 8.1.8 (#344)
Bumps [io.github.goooler.shadow](https://github.com/Goooler/shadow) from 8.1.7 to 8.1.8.
- [Release notes](https://github.com/Goooler/shadow/releases)
- [Commits](https://github.com/Goooler/shadow/compare/v8.1.7...v8.1.8)

---
updated-dependencies:
- dependency-name: io.github.goooler.shadow
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-01 15:23:08 +02:00
Michel Elkenwaat 2b2b481c14 Globalize and simplify per-platform injection 2024-06-30 13:41:07 +02:00
Michel Elkenwaat e0cc9bdab3 Simplify client brand reading for 1.8+ 2024-06-30 13:17:07 +02:00
Jones 8aa05563d5 Add support for H2 databases (#342)
* build(deps): bump MySQL driver from 8.3.0 to 8.4.0

* build(deps): bump mariadb-java-client from 3.3.3 to 3.4.0

* feat: add h2 local database support

* style: fix javadoc for db type adapters [ci skip]
2024-06-30 12:40:28 +02:00
28 changed files with 484 additions and 120 deletions

No files matched your search

+1
View File
@@ -49,6 +49,7 @@ allprojects {
relocate("com.mysql", "xyz.jonesdev.sonar.libs.mysql")
relocate("org.mariadb", "xyz.jonesdev.sonar.libs.mariadb")
relocate("xyz.jonesdev.capja", "xyz.jonesdev.sonar.libs.capja")
relocate("org.h2", "xyz.jonesdev.sonar.libs.h2")
// Exclude unnecessary metadata information
exclude("META-INF/*/**")
+1 -1
View File
@@ -1,3 +1,3 @@
group=xyz.jonesdev.sonar
version=2.0.35
version=2.0.37
description=A lightweight, effective, and easy-to-use anti-bot plugin for Velocity, BungeeCord, and Bukkit.
+2 -2
View File
@@ -7,7 +7,7 @@ libby = "2.0.0-SNAPSHOT"
[plugins]
indra-git = "net.kyori.indra.git:3.1.3"
shadow = "io.github.goooler.shadow:8.1.7"
shadow = "io.github.goooler.shadow:8.1.8"
[libraries]
netty = { module = "io.netty:netty-all", version.ref = "netty" }
@@ -27,7 +27,7 @@ bstats-bukkit = { module = "org.bstats:bstats-bukkit", version.ref = "bstats" }
bstats-velocity = { module = "org.bstats:bstats-velocity", version.ref = "bstats" }
caffeine = "com.github.ben-manes.caffeine:caffeine:3.1.8"
ormlite = "com.j256.ormlite:ormlite-jdbc:6.1"
lombok = "org.projectlombok:lombok:1.18.32"
lombok = "org.projectlombok:lombok:1.18.34"
capja = "xyz.jonesdev:capja:1.0.3"
bungeecord = "net.md_5:bungeecord-proxy:master-SNAPSHOT"
velocity = "com.velocitypowered:velocity-proxy:3.3.0-SNAPSHOT"
@@ -27,6 +27,7 @@ import org.jetbrains.annotations.Nullable;
import xyz.jonesdev.sonar.api.Sonar;
import xyz.jonesdev.sonar.api.command.SonarCommand;
import xyz.jonesdev.sonar.api.logger.LoggerWrapper;
import xyz.jonesdev.sonar.api.ormlite.H2DatabaseTypeAdapter;
import xyz.jonesdev.sonar.api.ormlite.MariaDbDatabaseTypeAdapter;
import xyz.jonesdev.sonar.api.ormlite.MysqlDatabaseTypeAdapter;
import xyz.jonesdev.sonar.api.webhook.DiscordWebhook;
@@ -45,6 +46,7 @@ import java.util.regex.Pattern;
public final class SonarConfiguration {
@Getter
private final SimpleYamlConfig generalConfig, messagesConfig, webhookConfig;
@Getter
private final File languageFile, pluginFolder;
static final LoggerWrapper LOGGER = new LoggerWrapper() {
@@ -163,6 +165,7 @@ public final class SonarConfiguration {
// Database
database.type = Database.Type.valueOf(generalConfig.getString("database.type").toUpperCase());
database.filename = generalConfig.getString("database.filename");
database.host = generalConfig.getString("database.host");
database.port = generalConfig.getInt("database.port");
database.name = generalConfig.getString("database.name");
@@ -486,6 +489,7 @@ public final class SonarConfiguration {
public enum Type {
MYSQL("jdbc:mysql://%s:%d/%s", new MysqlDatabaseTypeAdapter()),
MARIADB("jdbc:mariadb://%s:%d/%s", new MariaDbDatabaseTypeAdapter()),
H2("jdbc:h2:file:%s", new H2DatabaseTypeAdapter()),
NONE(null, null);
private final String connectionString;
@@ -493,6 +497,7 @@ public final class SonarConfiguration {
}
private Type type;
private String filename;
private String host;
private int port;
private String name;
@@ -33,6 +33,7 @@ import xyz.jonesdev.sonar.api.config.SonarConfiguration;
import xyz.jonesdev.sonar.api.logger.LoggerWrapper;
import xyz.jonesdev.sonar.api.model.VerifiedPlayer;
import java.io.File;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.Instant;
@@ -81,16 +82,24 @@ public final class VerifiedPlayerController {
return;
}
// Initialize MySQL/MariaDB driver
database.getType().getDatabaseType().loadDriver();
try {
connectionSource = new JdbcPooledConnectionSource(
String.format(database.getType().getConnectionString(),
database.getHost(), database.getPort(), database.getName()),
String jdbcURL = String.format(database.getType().getConnectionString(),
database.getHost(), database.getPort(), database.getName());
// H2 has a different JDBC URL layout
// https://www.codejava.net/java-se/jdbc/connect-to-h2-database-examples
if (cachedDatabaseType == SonarConfiguration.Database.Type.H2) {
final File file = new File(Sonar.get().getConfig().getPluginFolder(),
Sonar.get().getConfig().getDatabase().getFilename());
jdbcURL = String.format(database.getType().getConnectionString(), file.getAbsolutePath());
}
connectionSource = new JdbcPooledConnectionSource(jdbcURL,
database.getUsername(), database.getPassword(), database.getType().getDatabaseType());
// Create table
// Create database table
try {
TableUtils.createTableIfNotExists(connectionSource, VerifiedPlayer.class);
} catch (SQLException exception) {
@@ -126,7 +135,6 @@ public final class VerifiedPlayerController {
// The connection source will always be null if the database type is NONE.
if (connectionSource != null) {
try {
// Properly close the connection
connectionSource.close();
} catch (Exception exception) {
LOGGER.error("Error closing database: {}", exception);
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2023-2024 Sonar Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package xyz.jonesdev.sonar.api.ormlite;
import com.j256.ormlite.jdbc.db.H2DatabaseType;
import org.jetbrains.annotations.NotNull;
public final class H2DatabaseTypeAdapter extends H2DatabaseType {
// We need to override the default driver class name
// to use the custom relocated H2 driver
@Override
protected String @NotNull [] getDriverClassNames() {
return new String[]{"xyz.jonesdev.sonar.libs.h2.Driver"};
}
}
@@ -22,11 +22,8 @@ import org.jetbrains.annotations.NotNull;
public final class MariaDbDatabaseTypeAdapter extends MariaDbDatabaseType {
/**
* We need to override the default driver class name
* in order to use the custom relocated MariaDB driver
* Read more: {@link MysqlDatabaseTypeAdapter}
*/
// We need to override the default driver class name
// to use the custom relocated MariaDB driver
@Override
protected String @NotNull [] getDriverClassNames() {
return new String[]{"xyz.jonesdev.sonar.libs.mariadb.jdbc.Driver"};
@@ -22,11 +22,8 @@ import org.jetbrains.annotations.NotNull;
public final class MysqlDatabaseTypeAdapter extends MysqlDatabaseType {
/**
* We need to override the default driver class name
* in order to use the custom relocated MySQL driver
* Read more: {@link MariaDbDatabaseTypeAdapter}
*/
// We need to override the default driver class name
// to use the custom relocated MySQL driver
@Override
protected String @NotNull [] getDriverClassNames() {
return new String[]{"xyz.jonesdev.sonar.libs.mysql.cj.jdbc.Driver"};
@@ -28,7 +28,7 @@ import org.jetbrains.annotations.Nullable;
import xyz.jonesdev.sonar.api.SonarPlatform;
import xyz.jonesdev.sonar.api.logger.LoggerWrapper;
import xyz.jonesdev.sonar.bungee.command.BungeeSonarCommand;
import xyz.jonesdev.sonar.bungee.fallback.FallbackInjectionHelper;
import xyz.jonesdev.sonar.bungee.fallback.FallbackBungeeInjector;
import xyz.jonesdev.sonar.common.boot.SonarBootstrap;
import java.util.UUID;
@@ -96,7 +96,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
FallbackInjectionHelper.inject();
FallbackBungeeInjector.inject();
}
@Override
@@ -35,9 +35,9 @@ import java.net.InetSocketAddress;
import static net.md_5.bungee.netty.PipelineUtils.*;
public final class FallbackChannelHandler extends FallbackChannelHandlerAdapter {
public final class FallbackBungeeChannelHandler extends FallbackChannelHandlerAdapter {
public FallbackChannelHandler(final @NotNull Channel channel) {
public FallbackBungeeChannelHandler(final @NotNull Channel channel) {
super(channel);
}
@@ -54,6 +54,7 @@ public final class FallbackChannelHandler extends FallbackChannelHandlerAdapter
@Override
public void channelRead(final @NotNull ChannelHandlerContext ctx, final Object msg) throws Exception {
// TODO: put this into a separate handler
// Intercept any packets processed by BungeeCord
if (listenForPackets && msg instanceof PacketWrapper) {
final PacketWrapper packetWrapper = (PacketWrapper) msg;
@@ -23,11 +23,15 @@ import lombok.experimental.UtilityClass;
import net.md_5.bungee.netty.PipelineUtils;
import sun.misc.Unsafe;
import xyz.jonesdev.sonar.api.ReflectiveOperationException;
import xyz.jonesdev.sonar.common.fallback.injection.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_HANDLER;
@UtilityClass
public class FallbackInjectionHelper {
public class FallbackBungeeInjector {
public void inject() {
try {
final Field childField = PipelineUtils.class.getField("SERVER_CHILD");
@@ -35,7 +39,9 @@ public class FallbackInjectionHelper {
// Make sure to store the original channel initializer
final ChannelInitializer<Channel> originalInitializer = PipelineUtils.SERVER_CHILD;
final ChannelInitializer<Channel> fallbackInitializer = new FallbackChannelInitializer(originalInitializer);
final ChannelInitializer<Channel> fallbackInitializer = new FallbackInjectedChannelInitializer(
originalInitializer, pipeline -> pipeline.addAfter(PACKET_DECODER, FALLBACK_HANDLER,
new FallbackBungeeChannelHandler(pipeline.channel())));
final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
@@ -61,15 +61,22 @@ class LibraryLoader {
Library.builder()
.groupId("com{}mysql")
.artifactId("mysql-connector-j")
.version("8.3.0")
.version("8.4.0")
.relocate("com{}mysql", "xyz{}jonesdev{}sonar{}libs{}mysql")
.build(),
// MariaDB driver
Library.builder()
.groupId("org{}mariadb{}jdbc")
.artifactId("mariadb-java-client")
.version("3.3.3")
.version("3.4.0")
.relocate("org{}mariadb", "xyz{}jonesdev{}sonar{}libs{}mariadb")
.build(),
// H2 driver
Library.builder()
.groupId("com{}h2database")
.artifactId("h2")
.version("2.1.214")
.relocate("org{}h2", "xyz{}jonesdev{}sonar{}libs{}h2")
.build()
);
@@ -15,22 +15,21 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package xyz.jonesdev.sonar.bungee.fallback;
package xyz.jonesdev.sonar.common.fallback.injection;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import lombok.RequiredArgsConstructor;
import xyz.jonesdev.sonar.api.ReflectiveOperationException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import static net.md_5.bungee.netty.PipelineUtils.PACKET_DECODER;
import static xyz.jonesdev.sonar.api.fallback.FallbackPipelines.FALLBACK_HANDLER;
import java.util.function.Consumer;
@RequiredArgsConstructor
public final class FallbackChannelInitializer extends ChannelInitializer<Channel> {
public final class FallbackInjectedChannelInitializer extends ChannelInitializer<Channel> {
private static final MethodHandle INIT_CHANNEL_METHOD;
static {
@@ -43,6 +42,7 @@ public final class FallbackChannelInitializer extends ChannelInitializer<Channel
}
private final ChannelInitializer<Channel> originalChannelInitializer;
private final Consumer<ChannelPipeline> sonarPipelineInjector;
@Override
protected void initChannel(final Channel channel) throws Exception {
@@ -53,8 +53,9 @@ public final class FallbackChannelInitializer extends ChannelInitializer<Channel
throw new ReflectiveOperationException(throwable);
}
if (channel.pipeline().get(PACKET_DECODER) != null) {
channel.pipeline().addAfter(PACKET_DECODER, FALLBACK_HANDLER, new FallbackChannelHandler(channel));
// Inject Sonar's channel handler into the pipeline
if (channel.isActive()) {
sonarPipelineInjector.accept(channel.pipeline());
}
}
}
@@ -121,22 +121,22 @@ public abstract class FallbackSessionHandler implements FallbackPacketListener {
}
protected final void validateClientBrand(final byte @NotNull [] data) {
// No need to check for empty brands since ProtocolUtil#readBrandMessage
// already performs invalid string checks by default.
String read = new String(data, StandardCharsets.UTF_8);
checkState(read.length() > 1, "client brand is too short");
// Check if the client brand is too short. It has to have at least 2 bytes.
checkState(data.length > 1, "client brand is too short");
// Check if the decoded client brand string is too long
checkState(data.length < Sonar.get().getConfig().getVerification().getBrand().getMaxLength(),
"client brand contains too much data: " + data.length);
// https://discord.com/channels/923308209769426994/1116066363887321199/1256929441053933608
String brand = new String(data, StandardCharsets.UTF_8);
// Remove the invalid character at the beginning of the client brand
if (user.getProtocolVersion().compareTo(MINECRAFT_1_8) >= 0) {
read = read.substring(1);
brand = brand.substring(1);
}
// Check if the decoded client brand string is too long
checkState(read.length() < Sonar.get().getConfig().getVerification().getBrand().getMaxLength(),
"client brand is too long: " + read.length());
// Check for illegal client brands
checkState(!read.equals("Vanilla"), "illegal client brand: " + read);
checkState(!brand.equals("Vanilla"), "illegal client brand: " + brand);
// Regex pattern for validating client brands
final Pattern pattern = Sonar.get().getConfig().getVerification().getBrand().getValidRegex();
checkState(pattern.matcher(read).matches(), "client brand does not match pattern");
checkState(pattern.matcher(brand).matches(), "client brand does not match pattern");
}
protected final void validateClientLocale(final @NotNull String locale) {
@@ -43,8 +43,10 @@ attack-tracker:
# Nicméně, IP adresy nemůžou být vystopovány zpátky k hráči, protože Sonar používá UUID na ukládní místo jmeno hráčů
database:
# Typy datábázi se kterýma Sonar bude pracovat
# Typy: NONE, MYSQL, MARIADB
# Typy: NONE, MYSQL, MARIADB, H2
type: NONE
# Soubor v adresáři zásuvných modulů Sonaru použitý pro databázi H2
filename: verified_players
# Host adresa pro verifikování hráčů s SQL databází
host: localhost
# Port pro verifikaci s databází
@@ -40,8 +40,10 @@ attack-tracker:
# Beachte: IP adressen werden im Klartext gespeichert. Du bist für die Sicherheit der Datenbank verantwortlich
database:
# Typ von Datenbank die Sonar verwenden soll, um verifizierte Spielerdaten zu speichern
# Mögliche Typen: NONE, MYSQL, MARIADB
# Mögliche Typen: NONE, MYSQL, MARIADB, H2
type: NONE
# Datei im Plugin-Verzeichnis von Sonar, die für die H2-Datenbank verwendet wird
filename: verified_players
# HOST für die Authentifizierung mit der SQL Datenbank
host: localhost
# Port für die Authentifizierung mit der SQL Datenbank
@@ -42,8 +42,10 @@ attack-tracker:
# Note: IP addresses are saved in plain text. You are responsible for keeping your database safe!
database:
# Type of database Sonar uses to store verified players
# Possible types: NONE, MYSQL, MARIADB
# Possible types: NONE, MYSQL, MARIADB, H2
type: NONE
# File in Sonar's plugin directory used for the H2 database
filename: verified_players
# Host address for authenticating with the SQL database
host: localhost
# Port for authenticating with the SQL database
@@ -42,8 +42,10 @@ attack-tracker:
# Remarque : Les adresses IP sont enregistrées en texte brut. Vous êtes responsable de la sécurité de votre base de données !
database:
# Type de base de données utilisé par Sonar pour stocker les joueurs vérifiés
# Types possibles : NONE, MYSQL, MARIADB
# Types possibles : NONE, MYSQL, MARIADB, H2
type: NONE
# Fichier dans le répertoire des plugins de Sonar utilisé pour la base de données H2
filename: verified_players
# Adresse de l'hôte pour l'authentification avec la base de données SQL
host: localhost
# Port pour l'authentification avec la base de données SQL
@@ -42,8 +42,10 @@ attack-tracker:
# შენიშვნა: IP მისამართები ინახება უბრალო ტექსტში. თქვენ ხართ პასუხისმგებელი თქვენი მონაცემთა ბაზის უსაფრთხოებაზე!
database:
# მონაცემთა ბაზის ტიპები, რომლებიც Sonar-ს შეუძლია გამოიყენოს ვერიფირებული მოთამაშეების შესანახად
# შესაძლო ტიპები: NONE, MYSQL, MARIADB
# შესაძლო ტიპები: NONE, MYSQL, MARIADB, H2
type: NONE
# File in Sonar's plugin directory used for the H2 database
filename: verified_players
# ჰოსტის მისამართი SQL მონაცემთა ბაზის ავთენტიფიკაციისთვის
host: localhost
# პორტი SQL მონაცემთა ბაზის ავთენტიფიკაციისთვის
@@ -42,8 +42,10 @@ attack-tracker:
# Let op: IP-adressen worden opgeslagen in platte tekst. U bent verantwoordelijk voor het veilig houden van uw database!
database:
# Type database dat Sonar gebruikt om geverifieerde spelers op te slaan
# Mogelijke typen: NONE, MYSQL, MARIADB
# Mogelijke typen: NONE, MYSQL, MARIADB, H2
type: NONE
# Bestand in de plugin-directory van Sonar dat wordt gebruikt voor de H2-database
filename: verified_players
# Adres voor authenticatie met de SQL-database
host: localhost
# Poort voor authenticatie met de SQL-database
@@ -42,8 +42,10 @@ attack-tracker:
# Примечание: IP-адреса сохраняются в виде обычного текста. Вы сами отвечаете за сохранность своей базы данных!
database:
# Тип базы данных, которую Sonar использует для хранения проверенных игроков.
# Возможные типы: NONE, MYSQL, MARIADB
# Возможные типы: NONE, MYSQL, MARIADB, H2
type: NONE
# Файл в каталоге плагинов Sonar, используемый для базы данных H2
filename: verified_players
# Адрес хоста для аутентификации в базе данных SQL
host: localhost
# Порт для аутентификации с базой данных SQL
@@ -42,8 +42,10 @@ attack-tracker:
# 提示: IP 地址将以纯文本形式保存 请确保您的数据库安全!
database:
# 要连接到的数据库类型
# 可用类型: NONE (不使用数据库), MYSQL, MARIADB
# 可用类型: NONE (不使用数据库), MYSQL, MARIADB, H2
type: NONE
# Sonar 插件目录中用于 H2 数据库的文件
filename: verified_players
# 数据库的地址
host: localhost
# 数据库的端口
@@ -0,0 +1,288 @@
#
# /$$$$$$
# /$$__ $$
# | $$ \__/ /$$$$$$ /$$$$$$$ /$$$$$$ /$$$$$$
# | $$$$$$ /$$__ $$| $$__ $$ |____ $$ /$$__ $$
# \____ $$| $$ \ $$| $$ \ $$ /$$$$$$$| $$ \__/
# /$$ \ $$| $$ | $$| $$ | $$ /$$__ $$| $$
# | $$$$$$/| $$$$$$/| $$ | $$| $$$$$$$| $$
# \______/ \______/ |__/ |__/ \_______/|__/
#
# Butuh bantuan atau memiliki pertanyaan? https://jonesdev.xyz/discord
# Kode sumber: https://github.com/jonesdevelopment/sonar
# Hak cipta Jones Development dan Kontributor Sonar, dilisensikan di bawah GPL-3.0
#
# Placeholder untuk setiap '<prefix>' dalam file konfigurasi ini
prefix: <b><yellow>Sonar</b> <gray>» <white>
# Placeholder untuk setiap '<support-url>' dalam file konfigurasi ini
support-url: https://jonesdev.xyz/discord/
# Placeholder untuk setiap '<header>' dalam file konfigurasi ini
header:
- <b><yellow>Sonar</b><reset>
- ''
# Placeholder untuk setiap '<footer>' dalam file konfigurasi ini
footer:
- <gray>Jika Anda yakin ini adalah kesalahan, hubungi administrator.
# Pesan pemutusan yang ditampilkan saat seseorang bergabung, tetapi ada terlalu banyak pemain online dengan alamat IP mereka
too-many-online-per-ip:
- '<header>'
- <red>Ada terlalu banyak pemain online dengan alamat IP Anda.
- '<footer>'
# Terjemahan untuk perintah
commands:
# Pesan yang ditampilkan saat pemain mencoba menjalankan /sonar tanpa izin
no-permission: '<prefix><red>Anda tidak memiliki izin untuk menjalankan perintah ini.'
# Pesan yang ditampilkan saat seseorang menggunakan perintah dengan salah
incorrect-usage: '<prefix><red>Penggunaan: /sonar <subcommand-usage>'
# Pesan yang ditampilkan saat seseorang memberikan alamat IP yang tidak valid (Format tidak valid)
invalid-ip-address: '<prefix>Alamat IP yang Anda berikan tampaknya tidak valid.'
# Pesan yang ditampilkan saat konsol menjalankan perintah yang hanya untuk pemain
player-only: '<prefix><red>Anda hanya dapat menjalankan perintah ini sebagai pemain.'
# Pesan yang ditampilkan saat pemain menjalankan perintah yang hanya untuk konsol
console-only: '<prefix><red>Demi alasan keamanan, Anda hanya dapat menjalankan perintah ini melalui konsol.'
# Pesan yang ditampilkan saat pemain tidak memiliki izin untuk menjalankan sub-perintah tertentu
subcommand-no-permission: '<prefix><red>Anda tidak memiliki izin untuk menjalankan sub-perintah ini. <gray>(<permission>)'
# Terjemahan untuk '/sonar'
main:
# Pesan informasi yang ditampilkan di atas segalanya saat menjalankan perintah utama
header:
- <yellow>Menjalankan <b>Sonar</b> <version> di <platform>.
- <gray><click:open_url:'https://github.com/jonesdevelopment/sonar'>(C) <copyright-year> Jones Development dan Kontributor Sonar</click>
- ''
- <gray>Butuh bantuan atau memiliki pertanyaan?
- <white><click:open_url:'https://jonesdev.xyz/discord/'><hover:show_text:'(Klik untuk membuka Discord)'>Buka tiket di Discord </hover></click><click:open_url:'https://github.com/jonesdevelopment/sonar/issues'><hover:show_text:'(Klik untuk membuka GitHub)'>atau buka masalah baru di GitHub.
- ''
# Format daftar sub-perintah yang ditampilkan saat menjalankan perintah utama
subcommands: '<suggest-subcommand><hover:show_text:''<gray>Hanya pemain: </gray><only-players><br><gray>Membutuhkan konsol: </gray><only-console><br><gray>Izin: </gray><white><permission><br><gray>Alias: </gray><aliases>''><dark_aqua> ▪ <gray>/sonar <subcommand> <white><description></hover></suggest-subcommand>'
# Format untuk nilai ya (centang) dan tidak (silang) dalam teks hover sub-perintah
tick: '<green>✔</green>'
cross: '<red>✗</red>'
# Terjemahan untuk '/sonar reload'
reload:
# Deskripsi untuk sub-perintah ini
description: "Muat ulang semua konfigurasi"
# Pesan yang ditampilkan saat seseorang mulai memuat ulang Sonar
start: '<prefix>Memuat ulang Sonar...'
# Pesan yang ditampilkan saat Sonar selesai memuat ulang
finish: '<prefix><green>Berhasil memuat ulang <gray>(<time-taken>ms)'
# Terjemahan untuk '/sonar verbose'
verbose:
# Deskripsi untuk sub-perintah ini
description: "Aktifkan atau nonaktifkan verbose Sonar"
# Pesan yang ditampilkan saat pemain berlangganan verbose Sonar
subscribe: '<prefix>Anda sekarang melihat verbose Sonar.'
# Pesan yang ditampilkan saat pemain berhenti berlangganan verbose Sonar
unsubscribe: '<prefix>Anda tidak lagi melihat verbose Sonar.'
# Terjemahan untuk '/sonar notify'
notify:
# Deskripsi untuk sub-perintah ini
description: "Aktifkan atau nonaktifkan notifikasi serangan"
# Pesan yang ditampilkan saat pemain berlangganan notifikasi serangan Sonar
subscribe: '<prefix>Anda sekarang melihat notifikasi serangan Sonar.'
# Pesan yang ditampilkan saat pemain berhenti berlangganan notifikasi serangan Sonar
unsubscribe: '<prefix>Anda tidak lagi melihat notifikasi serangan Sonar.'
# Terjemahan untuk '/sonar blacklist'
blacklist:
# Deskripsi untuk sub-perintah ini
description: "Kelola alamat IP yang masuk daftar hitam"
# Pesan yang ditampilkan saat seseorang mencoba mengosongkan daftar hitam tetapi kosong
empty: '<prefix>Daftar hitam saat ini kosong. Oleh karena itu, tidak ada alamat IP yang dihapus dari daftar hitam.'
# Pesan yang ditampilkan saat seseorang mengosongkan daftar hitam
clear: '<prefix>Anda berhasil menghapus total <amount> alamat IP dari daftar hitam.'
# Pesan yang ditampilkan saat seseorang memeriksa ukuran daftar hitam
size: '<prefix>Daftar hitam saat ini berisi <amount> alamat IP.'
# Pesan yang ditampilkan saat seseorang menghapus alamat IP dari daftar hitam
remove: '<prefix>Berhasil menghapus <ip> dari daftar hitam.'
# Pesan yang ditampilkan saat seseorang menambahkan alamat IP ke daftar hitam
add: '<prefix>Berhasil menambahkan <ip> ke daftar hitam.'
# Pesan yang ditampilkan saat seseorang menambahkan alamat IP ke daftar hitam yang diverifikasi
add-warning: '<prefix><red>Peringatan: <white><ip> saat ini di daftar putih. Pertimbangkan untuk menghapus alamat IP dari daftar pemain yang diverifikasi untuk menghindari masalah potensial.'
# Pesan yang ditampilkan saat seseorang menambahkan alamat IP ke daftar hitam, tetapi sudah masuk daftar hitam
ip-duplicate: '<prefix>Alamat IP yang Anda berikan sudah masuk daftar hitam.'
# Pesan yang ditampilkan saat seseorang menghapus alamat IP dari daftar hitam, tetapi tidak masuk daftar hitam
ip-not-found: '<prefix>Alamat IP yang Anda berikan tidak masuk daftar hitam.'
# Translations for '/sonar verified'
verified:
# Deskripsi untuk sub-perintah ini
description: "Kelola alamat IP yang diverifikasi"
# Pesan yang ditampilkan saat seseorang mencoba mengosongkan daftar pemain yang diverifikasi tetapi kosong
empty: '<prefix>Daftar pemain yang diverifikasi saat ini kosong. Oleh karena itu, tidak ada pemain yang dibatalkan verifikasinya.'
# Pesan yang ditampilkan saat seseorang mengosongkan daftar pemain yang diverifikasi
clear: '<prefix>Anda berhasil membatalkan verifikasi total <amount> pemain unik.'
# Pesan yang ditampilkan saat seseorang memeriksa ukuran daftar pemain yang diverifikasi
size: '<prefix>Saat ini ada <amount> pemain unik yang diverifikasi.'
# Pesan yang ditampilkan saat seseorang membatalkan verifikasi alamat IP
remove: '<prefix>Berhasil membatalkan verifikasi <ip>.'
# Pesan yang ditampilkan saat seseorang memverifikasi alamat IP
add: '<prefix>Berhasil memverifikasi <ip>.'
# Pesan yang ditampilkan saat seseorang mencoba memverifikasi alamat IP/UUID yang sudah diverifikasi
already: '<prefix>Alamat IP dan UUID yang Anda berikan sudah diverifikasi.'
# Pesan yang ditampilkan saat seseorang membatalkan verifikasi alamat IP, tetapi tidak diverifikasi
ip-not-found: '<prefix>Alamat IP yang Anda berikan tidak diverifikasi.'
# Pesan yang ditampilkan saat seseorang melihat riwayat verifikasi alamat IP
history: '<prefix><yellow>UUID sebelumnya untuk <ip>:'
history-entry: ' <gray>▪ <white><uuid>'
# Terjemahan untuk '/sonar statistics'
statistics:
# Deskripsi untuk sub-perintah ini
description: "Tampilkan statistik sesi server ini"
# Pesan informasi yang ditampilkan di atas segalanya saat melihat statistik
header: '<prefix>Menampilkan statistik <statistics-type> untuk sesi ini:'
# Pesan yang ditampilkan saat pemain mencoba melihat statistik yang tidak dikenal
unknown-type: '<prefix><red>Tipe statistik tidak dikenal! Statistik yang tersedia: <gray><statistics>'
# Format pesan statistik umum
general:
- ' <dark_aqua>▪ <gray>Alamat IP yang diverifikasi: <white><verified>'
- ' <dark_aqua>▪ <gray>Memverifikasi alamat IP: <white><verifying>'
- ' <dark_aqua>▪ <gray>Alamat IP yang masuk daftar hitam: <white><blacklisted>'
- ' <dark_aqua>▪ <gray>Antrian login saat ini: <white><queued>'
- ' <dark_aqua>▪ <gray>Total join non-unik: <white><total-joins>'
- ' <dark_aqua>▪ <gray>Total percobaan verifikasi: <white><total-attempts>'
- ' <dark_aqua>▪ <gray>Total verifikasi yang gagal: <white><total-failed>'
- ' <dark_aqua>▪ <gray>Waktu aktif server: <white><server-uptime>'
# Format pesan statistik CPU
cpu:
- ' <dark_aqua>▪ <gray>Penggunaan CPU proses saat ini: <white><process-cpu>%'
- ' <dark_aqua>▪ <gray>Penggunaan CPU sistem saat ini: <white><system-cpu>%'
- ' <dark_aqua>▪ <gray>Penggunaan CPU proses per-core: <white><average-process-cpu>%'
- ' <dark_aqua>▪ <gray>Penggunaan CPU sistem per-core: <white><average-system-cpu>%'
- ' <dark_aqua>▪ <gray>Total jumlah vCPU: <white><virtual-core-count>'
# Format pesan statistik memori
memory:
- ' <dark_aqua>▪ <gray>Total memori bebas: <white><free-memory>'
- ' <dark_aqua>▪ <gray>Total memori terpakai: <white><used-memory>'
- ' <dark_aqua>▪ <gray>Total memori maksimum: <white><max-memory>'
- ' <dark_aqua>▪ <gray>Total memori dialokasikan: <white><total-memory>'
# Format pesan statistik jaringan
network:
- ' <dark_aqua>▪ <gray>Bandwidth masuk saat ini: <white><incoming-traffic>'
- ' <dark_aqua>▪ <gray>Bandwidth keluar saat ini: <white><outgoing-traffic>'
- ' <dark_aqua>▪ <gray>Total bandwidth masuk: <white><incoming-traffic-ttl>'
- ' <dark_aqua>▪ <gray>Total bandwidth keluar: <white><outgoing-traffic-ttl>'
# Terjemahan untuk '/sonar dump'
dump:
# Deskripsi untuk sub-perintah ini
description: "Cetak informasi pengembang"
# Format informasi yang dikirim ke konsol
log: "Dump yang dihasilkan: <dumped-json-data>"
# Terjemahan untuk semua pesan selama proses verifikasi
verification:
# Terjemahan untuk semua pesan debug selama proses verifikasi
logs:
# Pesan yang dicatat ke konsol setiap kali pemain baru bergabung ke server
connection: '<username><ip> (<protocol>) telah terhubung.'
# Pesan yang dicatat ke konsol setiap kali pemain gagal verifikasi
failed: '<ip> (<protocol>) telah gagal dalam pemeriksaan bot untuk: <reason>'
# Pesan yang dicatat ke konsol setiap kali pemain masuk daftar hitam
blacklisted: '<ip> (<protocol>) masuk daftar hitam karena terlalu banyak percobaan yang gagal'
# Pesan yang dicatat ke konsol setiap kali pemain berhasil diverifikasi
successful: '<username> berhasil diverifikasi dalam waktu (<time-taken>s!).'
# Pesan yang ditampilkan kepada pemain saat mereka seharusnya memasukkan CAPTCHA
captcha:
# Pesan yang ditampilkan kepada pemain saat mereka harus memasukkan jawaban untuk captcha
enter-code: '<prefix><green>Silakan masukkan kode di chat yang ditampilkan di peta.'
# Pesan yang ditampilkan kepada pemain saat mereka memasukkan jawaban yang salah di chat
incorrect: '<prefix><red>Anda memasukkan kode yang salah. Silakan coba lagi.'
# Pesan putus yang ditampilkan saat seseorang bergabung sementara captcha belum disiapkan
currently-preparing:
- '<header>'
- <yellow>Data anti-bot Anda belum disiapkan.
- <gray>Silakan tunggu beberapa detik sebelum mencoba verifikasi lagi.
- '<footer>'
# Pesan putus yang ditampilkan saat seseorang bergabung kembali terlalu cepat selama verifikasi
too-fast-reconnect:
- '<header>'
- <gold>Anda terhubung kembali terlalu cepat, coba lagi nanti.
- <gray>Silakan tunggu beberapa detik sebelum mencoba verifikasi lagi.
- '<footer>'
# Pesan putus yang ditampilkan saat seseorang bergabung tetapi sudah dalam proses verifikasi
already-verifying:
- '<header>'
- <red>Alamat IP Anda saat ini sedang diverifikasi.
- <red>Silakan tunggu beberapa detik sebelum mencoba verifikasi lagi.
- '<footer>'
# Pesan putus yang ditampilkan saat seseorang bergabung tetapi sudah antri untuk verifikasi
already-queued:
- '<header>'
- <red>Alamat IP Anda saat ini sedang antri untuk verifikasi.
- <red>Silakan tunggu beberapa menit sebelum mencoba verifikasi lagi.
- '<footer>'
# Pesan putus yang ditampilkan saat seseorang bergabung tetapi sedang masuk daftar hitam sementara
blacklisted:
- '<header>'
- <red>Anda saat ini ditolak untuk masuk ke server.
- <red>Silakan tunggu beberapa menit agar bisa bergabung ke server lagi.
- <gold>Positif palsu? <gray><support-url>
- '<footer>'
# Pesan putus yang ditampilkan saat seseorang bergabung dengan username yang tidak valid
invalid-username:
- '<header>'
- <red>Username Anda mengandung karakter yang tidak valid.
- '<footer>'
# Pesan putus yang ditampilkan saat seseorang bergabung dengan versi yang masuk daftar hitam
blacklisted-protocol:
- '<header>'
- <red>Anda menggunakan versi yang tidak diizinkan di server kami.
- <gold>Butuh bantuan masuk? <gray><support-url>
- '<footer>'
# Pesan putus yang ditampilkan saat seseorang berhasil verifikasi
success:
- '<header>'
- <green>Anda telah berhasil melewati verifikasi.
- <white>Anda sekarang dapat bermain di server saat Anda terhubung kembali.
# Pesan putus yang ditampilkan saat seseorang gagal verifikasi
failed:
- '<header>'
- <red>Anda telah gagal dalam verifikasi bot.
- <gray>Silakan tunggu beberapa detik sebelum mencoba verifikasi lagi.
- <gold>Butuh bantuan masuk? <gray><support-url>
- '<footer>'
# Terjemahan untuk pesan verbose actionbar Sonar
verbose:
layout:
# Tata letak untuk actionbar verbose Sonar
normal: '<prefix><gray>CPS <white><connections-per-second> <dark_aqua>╺ <gray>Logins/s <white><logins-per-second> <dark_aqua>╺ <gray>Verifikasi <white><verifying> <dark_aqua>| <green>⬆ <white><outgoing-traffic>/s <red>⬇ <white><incoming-traffic>/s <green><bold><animation>'
# Tata letak untuk actionbar verbose Sonar selama serangan
attack: '<prefix><gray>CPS <white><connections-per-second> <dark_aqua>╺ <gray>Logins/s <white><logins-per-second> <dark_aqua>╺ <gray>Antrian <white><queued> <dark_aqua>╺ <gray>Verifikasi <white><verifying> <dark_aqua>╺ <gray>Durasi <white><attack-duration> <dark_aqua>| <green>⬆ <white><outgoing-traffic>/s <red>⬇ <white><incoming-traffic>/s <green><bold><animation>'
# Animasi untuk action bar
# Alternatif:
# - ▙, ▛, ▜, ▟
# - ⬈, ⬊, ⬋, ⬉
animation:
- ◜
- ◝
- ◞
- ◟
# Terjemahan untuk pesan notifikasi serangan Sonar
notifications:
# Tata letak untuk judul notifikasi serangan Sonar
title: <yellow><b>Sonar<reset>
# Tata letak untuk subjudul notifikasi serangan Sonar
subtitle: <gray>Serangan bot terdeteksi
# Tata letak untuk notifikasi serangan Sonar di chat
chat:
- ''
- <yellow><b>Sonar<reset>
- ''
- <gray>Serangan bot terdeteksi.
- ' <dark_aqua>▪ <gray>Login per detik: <white><logins-per-second>'
- ' <dark_aqua>▪ <gray>Pemain yang diverifikasi: <white><verifying>'
- ' <dark_aqua>▪ <gray>Pemain yang antri: <white><queued>'
- ''
- <gray>Lihat lebih banyak informasi menggunakan <white><click:run_command:'/sonar verbose'><hover:show_text:'(Klik untuk menjalankan)'>/sonar verbose</hover></click><gray> atau <white><click:run_command:'/sonar stats'><hover:show_text:'(Klik untuk menjalankan)'>/sonar stats</hover></click><gray>.
- ''
@@ -0,0 +1,60 @@
#
# /$$$$$$
# /$$__ $$
# | $$ \__/ /$$$$$$ /$$$$$$$ /$$$$$$ /$$$$$$
# | $$$$$$ /$$__ $$| $$__ $$ |____ $$ /$$__ $$
# \____ $$| $$ \ $$| $$ \ $$ /$$$$$$$| $$ \__/
# /$$ \ $$| $$ | $$| $$ | $$ /$$__ $$| $$
# | $$$$$$/| $$$$$$/| $$ | $$| $$$$$$$| $$
# \______/ \______/ |__/ |__/ \_______/|__/
#
# Butuh bantuan atau punya pertanyaan? https://jonesdev.xyz/discord
# Kode sumber: https://github.com/jonesdevelopment/sonar
# Hak cipta Jones Development dan Kontributor Sonar, dilisensikan di bawah GPL-3.0
#
# Notifikasi serangan bot juga dapat dikirim ke server Discord Anda menggunakan webhook
webhook:
# URL dari webhook Discord (Atur ini ke '' untuk menonaktifkan webhook)
url: ''
# Nama pengguna pengirim webhook Discord
username: Sonar
# URL avatar pengirim webhook Discord (Atur ini ke '' untuk menonaktifkan)
avatar-url: ''
# Konten pesan webhook Discord (Atur ini ke '' untuk menonaktifkan)
# Anda dapat menggunakan ini untuk misalnya memberi ping ke anggota staf menggunakan <@userId>
# Jika Anda ingin memberi ping ke peran, Anda harus menggunakan <@&roleId>
content: ''
# Pesan embed webhook Discord yang dikirim ketika serangan telah berhenti
embed:
# Pesan footer kecil dari embed webhook Discord
footer:
# Konten pesan footer dari embed webhook Discord
text: © Jones Development dan Kontributor Sonar
# URL ikon pesan footer dari embed webhook Discord
icon-url: ''
# Judul dari embed webhook Discord
title: '✅ Serangan berhasil diatasi'
# URL yang dapat diklik dari judul embed webhook Discord
title-url: ''
# Deskripsi (konten) dari embed webhook Discord
description:
- Serangan pada server Anda telah berhasil diatasi.
- ''
- 'Mulai serangan: <t:<start-timestamp>:T>'
- 'Akhir serangan: <t:<end-timestamp>:T>'
- 'Durasi serangan: <attack-duration>'
- ''
- 'Penggunaan CPU proses puncak selama serangan: <peak-cpu>%'
- 'Penggunaan memori proses puncak selama serangan: <peak-memory>'
- 'Bot per detik puncak selama serangan: <peak-bps>'
- ''
- 'Alamat IP yang masuk daftar hitam selama serangan: <total-blacklisted>'
- 'Verifikasi yang gagal selama serangan: <total-failed>'
- 'Verifikasi yang berhasil selama serangan: <total-success>'
# Warna RGB dari embed webhook Discord
# Pemilih warna: https://www.rapidtables.com/web/color/RGB_Color.html
color:
red: 0
green: 255
blue: 0
@@ -28,7 +28,7 @@ import xyz.jonesdev.sonar.api.SonarPlatform;
import xyz.jonesdev.sonar.api.logger.LoggerWrapper;
import xyz.jonesdev.sonar.common.boot.SonarBootstrap;
import xyz.jonesdev.sonar.velocity.command.VelocitySonarCommand;
import xyz.jonesdev.sonar.velocity.fallback.FallbackInjectionHelper;
import xyz.jonesdev.sonar.velocity.fallback.FallbackVelocityInjector;
import java.util.UUID;
@@ -95,7 +95,7 @@ public final class SonarVelocity extends SonarBootstrap<SonarVelocityPlugin> {
getPlugin().getServer().getCommandManager().register("sonar", new VelocitySonarCommand());
// Make sure to inject into the server's connection handler
FallbackInjectionHelper.inject(getPlugin().getServer());
FallbackVelocityInjector.inject(getPlugin().getServer());
}
@Override
@@ -1,60 +0,0 @@
/*
* Copyright (C) 2023-2024 Sonar Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package xyz.jonesdev.sonar.velocity.fallback;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import lombok.RequiredArgsConstructor;
import xyz.jonesdev.sonar.api.ReflectiveOperationException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import static com.velocitypowered.proxy.network.Connections.MINECRAFT_DECODER;
import static xyz.jonesdev.sonar.api.fallback.FallbackPipelines.FALLBACK_HANDLER;
@RequiredArgsConstructor
public final class FallbackChannelInitializer extends ChannelInitializer<Channel> {
private static final MethodHandle INIT_CHANNEL_METHOD;
static {
try {
INIT_CHANNEL_METHOD = MethodHandles.privateLookupIn(ChannelInitializer.class, MethodHandles.lookup())
.findVirtual(ChannelInitializer.class, "initChannel", MethodType.methodType(void.class, Channel.class));
} catch (Throwable throwable) {
throw new ReflectiveOperationException(throwable);
}
}
private final ChannelInitializer<Channel> originalChannelInitializer;
@Override
protected void initChannel(final Channel channel) throws Exception {
// Invoke the original method
try {
INIT_CHANNEL_METHOD.invokeExact(originalChannelInitializer, channel);
} catch (Throwable throwable) {
throw new ReflectiveOperationException(throwable);
}
if (channel.pipeline().get(MINECRAFT_DECODER) != null) {
channel.pipeline().addAfter(MINECRAFT_DECODER, FALLBACK_HANDLER, new FallbackChannelHandler(channel));
}
}
}
@@ -29,14 +29,15 @@ import java.net.InetSocketAddress;
import static com.velocitypowered.proxy.network.Connections.*;
public final class FallbackChannelHandler extends FallbackChannelHandlerAdapter {
public final class FallbackVelocityChannelHandler extends FallbackChannelHandlerAdapter {
public FallbackChannelHandler(final @NotNull Channel channel) {
public FallbackVelocityChannelHandler(final @NotNull Channel channel) {
super(channel);
}
@Override
public void channelRead(final @NotNull ChannelHandlerContext ctx, final Object msg) throws Exception {
// TODO: put this into a separate handler
// Intercept any handshake packet by the client
if (listenForPackets) {
if (msg instanceof HandshakePacket handshake) {
@@ -17,7 +17,6 @@
package xyz.jonesdev.sonar.velocity.fallback;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.network.ConnectionManager;
import com.velocitypowered.proxy.network.ServerChannelInitializerHolder;
@@ -25,11 +24,15 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import lombok.experimental.UtilityClass;
import xyz.jonesdev.sonar.api.ReflectiveOperationException;
import xyz.jonesdev.sonar.common.fallback.injection.FallbackInjectedChannelInitializer;
import java.lang.reflect.Field;
import static com.velocitypowered.proxy.network.Connections.MINECRAFT_DECODER;
import static xyz.jonesdev.sonar.api.fallback.FallbackPipelines.FALLBACK_HANDLER;
@UtilityClass
public class FallbackInjectionHelper {
public class FallbackVelocityInjector {
private final Field CONNECTION_MANAGER_FIELD;
private final Field SERVER_CHANNEL_INITIALIZER_FIELD;
@@ -45,13 +48,15 @@ public class FallbackInjectionHelper {
}
}
public void inject(final ProxyServer proxyServer) {
public void inject(final Object proxyServer) {
try {
final ConnectionManager connectionManager = (ConnectionManager) CONNECTION_MANAGER_FIELD.get(proxyServer);
// Make sure to store the original channel initializer
final ChannelInitializer<Channel> originalInitializer = connectionManager.serverChannelInitializer.get();
final ChannelInitializer<Channel> fallbackInitializer = new FallbackChannelInitializer(originalInitializer);
final ChannelInitializer<Channel> fallbackInitializer = new FallbackInjectedChannelInitializer(
originalInitializer, pipeline -> pipeline.addAfter(MINECRAFT_DECODER, FALLBACK_HANDLER,
new FallbackVelocityChannelHandler(pipeline.channel())));
// Replace the original channel initializer
SERVER_CHANNEL_INITIALIZER_FIELD.set(connectionManager.getServerChannelInitializer(), fallbackInitializer);