Compare commits

...
19 Commits
Author SHA1 Message Date
rayzsyt 11ee68fa3f Register proantitab.bypass permission for LuckPerms to recognise 2026-10-15 23:16:13 +02:00
rayzsyt 2c3d3fac92 Removed unused space 2026-10-15 23:11:01 +02:00
rayzsyt e524f898da Changed approach how MiniMessage API is accessed and interacted with 2026-10-15 23:00:03 +02:00
Alihan c181cfbbc4 Merge pull request #42 from RVSkeLe/fix/communications
refactor: improve proxy-backend packet communication
2026-10-15 14:02:02 +02:00
RV_SkeLe 1110466235 refactor: improve proxy-backend packet communication
- Replace Java object serialization and XOR obfuscation with explicit binary packet encoding and AES-256-GCM encryption.
- Derive packet encryption keys from the shared token using HKDF-SHA-256, with backend-specific key separation.
- Remove legacy hashed-token authentication, introduce secure default token generation, and enforce packet size limits.
- Add JUnit tests for packet round-trips, authentication, tampering, malformed payloads, and boundary conditions.
2026-10-09 11:47:16 +02:00
rayzsyt a965dd231b Switched injection-return value to better work with and handle exceptions / rare cases 2026-10-03 18:04:10 +02:00
rayzsyt 6418e4f42a Switched to using hashed token instead of raw token 2026-09-30 16:24:09 +02:00
Alihan e71909fcd7 Merge pull request #41 from RVSkeLe/perf-improve
Plugin message refactor
2026-09-30 17:11:57 +03:00
RV_SkeLe c6f9fc8d3e refactor: plugin message handling 2026-09-30 12:16:29 +02:00
RV_SkeLe 88cb03c82e Add gitignore 2026-09-30 12:02:18 +02:00
rayzsyt b53678764a Update version 2026-09-20 03:14:07 +02:00
rayzsyt 1f7743d089 Removed debug messages 2026-09-20 03:13:04 +02:00
rayzsyt a60b6896f7 Fixed that KICK_ON_FAILURE wasn't listened at anymore 2026-08-31 06:17:26 +02:00
rayzsyt 34c33df136 Add sanity check just in case 2026-08-25 22:37:13 +02:00
rayzsyt ef63674b0c Changed approach to detect commands for tab-completion 2026-08-21 19:11:06 +02:00
rayzsyt 130af1547d Ignore IgnoreStarPermission feature and instead made PAT able to recognise negated PAT permissions 2026-08-20 19:47:52 +02:00
rayzsyt cdb17c1ea8 Add option for PAT to ignore the Star permission 2026-08-19 22:32:55 +02:00
rayzsyt 8f4f4b6438 Remove debug message 2026-08-19 20:44:55 +02:00
rayzsyt 3a0fc6e579 Add feature to show non-existing commands in tab-completion when whitelisted, no matter if they exist or not. (Only for proxies in PAT groups) 2026-08-19 20:44:42 +02:00
64 changed files with 1692 additions and 1123 deletions

No files matched your search

+29
View File
@@ -0,0 +1,29 @@
# Maven
/target/
# IntelliJ IDEA
/.idea/
*.iml
*.iws
*.ipr
# Eclipse
/.classpath
/.project
/.settings/
# VS Code
/.vscode/
# Local environment and logs
.env
.env.*
*.log
*.tmp
# OS files
.DS_Store
Thumbs.db
# Maven Shade Plugin
dependency-reduced-pom.xml
+69 -192
View File
@@ -6,7 +6,7 @@
<groupId>de.rayzs.pat</groupId>
<artifactId>ProAntiTab</artifactId>
<version>2.5.0</version>
<version>2.5.1</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
@@ -14,6 +14,11 @@
</properties>
<repositories>
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
@@ -24,14 +29,15 @@
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
@@ -77,17 +83,14 @@
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
</repository>
<repository>
<id>AlessioDP</id>
<url>https://repo.alessiodp.com/releases/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>dev.folia</groupId>
<artifactId>folia-api</artifactId>
<version>1.20.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
@@ -95,20 +98,6 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.19-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.2.0.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
@@ -116,77 +105,6 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-protocol</artifactId>
<version>1.16-R0.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.mojang</groupId>
<artifactId>authlib</artifactId>
<version>4.0.43</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.ElgarL</groupId>
<artifactId>groupmanager</artifactId>
<version>3.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.mojang</groupId>
<artifactId>brigadier</artifactId>
<version>1.1.8</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.mojang</groupId>
<artifactId>datafixerupper</artifactId>
<version>6.0.8</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.mojang</groupId>
<artifactId>logging</artifactId>
<version>1.1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-resolver-provider</artifactId>
<version>3.8.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-connector-basic</artifactId>
<version>1.7.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-transport-http</artifactId>
<version>1.7.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.18.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.github.waterfallmc</groupId>
<artifactId>waterfall-api</artifactId>
@@ -194,13 +112,6 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.william278</groupId>
<artifactId>papiproxybridge</artifactId>
<version>1.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.velocitypowered</groupId>
<artifactId>velocity-api</artifactId>
@@ -221,56 +132,64 @@
</exclusions>
</dependency>
<dependency>
<groupId>org.sonatype.sisu</groupId>
<artifactId>sisu-guice</artifactId>
<version>2.1.7</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spongepowered</groupId>
<artifactId>configurate-hocon</artifactId>
<version>4.1.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-minimessage</artifactId>
<version>5.2.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-api</artifactId>
<version>5.2.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-platform-api</artifactId>
<version>4.4.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-platform-bungeecord</artifactId>
<version>4.4.1</version>
<scope>compile</scope>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-platform-bukkit</artifactId>
<version>4.4.1</version>
<scope>compile</scope>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-legacy</artifactId>
<version>5.2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.2.0.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.11.4</version>
<scope>test</scope>
</dependency>
<!-- PLUGIN COMPATIBILITIES -->
<!-- Permission plugins -->
<dependency>
<groupId>net.luckperms</groupId>
<artifactId>api</artifactId>
<version>5.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.ElgarL</groupId>
<artifactId>groupmanager</artifactId>
<version>3.2</version>
<scope>provided</scope>
</dependency>
<!-- PAPI -->
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
@@ -279,12 +198,15 @@
</dependency>
<dependency>
<groupId>net.luckperms</groupId>
<artifactId>api</artifactId>
<version>5.5</version>
<groupId>net.william278</groupId>
<artifactId>papiproxybridge</artifactId>
<version>1.4</version>
<scope>provided</scope>
</dependency>
<!-- Extras -->
<dependency>
<groupId>com.viaversion</groupId>
<artifactId>viaversion-api</artifactId>
@@ -294,50 +216,5 @@
</dependencies>
<build>
<finalName>IgnoreThisJar</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>*.txt</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<finalName>ProAntiTab</finalName>
<shadedArtifactAttached>false</shadedArtifactAttached>
<shadedClassifierName>shaded</shadedClassifierName>
<artifactSet>
<excludes/>
</artifactSet>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -1,5 +1,6 @@
package de.rayzs.pat.api.storage;
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.TimeUnit;
@@ -61,7 +62,7 @@ public class Storage {
public static final List<UUID> NOTIFY_PLAYERS = new ArrayList<>();
public static String TOKEN = "", HASHED_TOKEN = "", CENSORED_TOKEN = "", SERVER_NAME = null, CURRENT_VERSION = "", NEWER_VERSION = "";
public static String TOKEN = "", CENSORED_TOKEN = "", SERVER_NAME = null, CURRENT_VERSION = "", NEWER_VERSION = "";
public static boolean OUTDATED = false, SEND_CONSOLE_NOTIFICATION = true;
public static boolean USE_PLACEHOLDERAPI = false, USE_PAPIPROXYBRIDGE = false, USE_VIAVERSION = false;
@@ -91,6 +92,12 @@ public class Storage {
Logger.warning("Read more about it here: https://rayzs.de/products/proantitab/nkppf");
}
private static String generateToken() {
byte[] bytes = new byte[32];
new SecureRandom().nextBytes(bytes);
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
}
public static PermissionPlugin getPermissionPlugin() {
return PERMISSION_PLUGIN;
}
@@ -140,11 +147,9 @@ public class Storage {
if (useDefaultToken) {
TOKEN = Reflection.isProxyServer()
? (String) Files.TOKEN.getOrSet("token", UUID.randomUUID().toString())
? (String) Files.TOKEN.getOrSet("token", generateToken())
: ConfigSections.Settings.HANDLE_THROUGH_PROXY.TOKEN;
}
HASHED_TOKEN = Objects.requireNonNullElse(StringUtils.hashString(TOKEN, "SHA-256"), TOKEN);
}
public static void loadConfig() {
@@ -339,6 +344,7 @@ public class Storage {
public static final BlockNamespaceCommandsSection BLOCK_NAMESPACE_COMMANDS = new BlockNamespaceCommandsSection();
public static final HandleThroughProxySection HANDLE_THROUGH_PROXY = new HandleThroughProxySection();
public static final HidePluginChannelsSection HIDE_PLUGIN_CHANNELS = new HidePluginChannelsSection();
public static final TabCompletionForNotExistingCommandsSection TAB_COMPLETION_FOR_NOT_EXISTING_COMMANDS = new TabCompletionForNotExistingCommandsSection();
public static final InjectionFailedSection INJECTION_FAILED = new InjectionFailedSection();
public static final PatchExploitSection PATCH_EXPLOITS = new PatchExploitSection();
public static final CustomBrandSection CUSTOM_BRAND = new CustomBrandSection();
@@ -413,7 +419,7 @@ public class Storage {
public static String findAndReplace(Player player, String request) {
String result = null, param = "";
if(USE_PLACEHOLDERAPI) {
if (USE_PLACEHOLDERAPI) {
for (PlaceholderStorage storage : PLACEHOLDERS) {
if (!storage.getRequest().startsWith(request)) continue;
@@ -429,7 +435,7 @@ public class Storage {
// Second time because of nested placeholders
if (result != null && result.contains("%")) {
result = PlaceholderReplacer.replace(player, result);
result = PlaceholderReplacer.applyPlaceholders(CommandSender.from(player), result);
}
}
@@ -0,0 +1,22 @@
package de.rayzs.pat.api.storage.config.settings;
import de.rayzs.pat.api.storage.storages.ConfigStorage;
import de.rayzs.pat.utils.Reflection;
import de.rayzs.pat.utils.configuration.helper.ConfigSectionHelper;
public class TabCompletionForNotExistingCommandsSection extends ConfigStorage {
public boolean ENABLED;
public TabCompletionForNotExistingCommandsSection() {
super("tab-completion-for-not-existing-commands");
}
@Override
public void load() {
super.load();
if (!Reflection.isProxyServer()) return;
ENABLED = new ConfigSectionHelper<Boolean>(this, null, false).getOrSet();
}
}
@@ -14,7 +14,7 @@ import de.rayzs.pat.utils.group.Group;
import de.rayzs.pat.utils.hooks.GroupManagerHook;
import de.rayzs.pat.utils.configuration.Configurator;
import de.rayzs.pat.utils.configuration.updater.ConfigUpdater;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.utils.permission.PermissionUtil;
import de.rayzs.pat.utils.hooks.ViaVersionHook;
import de.rayzs.pat.plugin.system.communication.Communicator;
@@ -95,7 +95,7 @@ public class BukkitLoader extends JavaPlugin implements PluginLoader {
if (Bukkit.getServer().getPluginManager().getPlugin("PlaceholderAPI") != null)
new PlaceholderHook().register();
MessageTranslator.initialize();
MessageHelper.initialize();
Communicator.initialize(new BukkitPluginMessageClient(), new BukkitCommunicationHandler());
BackendUpdater.initialize();
@@ -159,6 +159,11 @@ public class BukkitLoader extends JavaPlugin implements PluginLoader {
});
// Register permissions for LuckPerms to recognise.
addPermission("bypass");
for (final ProCommand command : CommandProcess.getCommands()) {
addPermission(command.getName());
}
@@ -190,7 +195,7 @@ public class BukkitLoader extends JavaPlugin implements PluginLoader {
public void onDisable() {
BackendUpdater.get().stop();
BukkitPacketAnalyzer.uninjectAll();
MessageTranslator.closeAudiences();
MessageHelper.closeAudiences();
}
public void registerCommand(String... commands) {
@@ -11,7 +11,7 @@ import de.rayzs.pat.plugin.packetanalyzer.proxy.BungeePacketAnalyzer;
import de.rayzs.pat.plugin.system.subargument.SubArgument;
import de.rayzs.pat.utils.configuration.Configurator;
import de.rayzs.pat.utils.configuration.updater.ConfigUpdater;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.utils.hooks.LuckPermsHook;
import de.rayzs.pat.plugin.system.communication.Communicator;
import de.rayzs.pat.utils.response.ResponseHandler;
@@ -64,7 +64,7 @@ public class BungeeLoader extends Plugin implements PluginLoader {
Storage.loadAll(true);
MessageTranslator.initialize();
MessageHelper.initialize();
GroupManager.initialize();
bStats.initialize(this);
@@ -121,7 +121,7 @@ public class BungeeLoader extends Plugin implements PluginLoader {
@Override
public void onDisable() {
BungeePacketAnalyzer.uninjectAll();
MessageTranslator.closeAudiences();
MessageHelper.closeAudiences();
}
@Override
@@ -18,7 +18,7 @@ import com.velocitypowered.api.scheduler.ScheduledTask;
import de.rayzs.pat.utils.configuration.Configurator;
import de.rayzs.pat.utils.configuration.updater.ConfigUpdater;
import de.rayzs.pat.plugin.command.impl.VelocityCommand;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.plugin.system.communication.Communicator;
import de.rayzs.pat.utils.hooks.LuckPermsHook;
import com.velocitypowered.api.proxy.ProxyServer;
@@ -40,16 +40,6 @@ import de.rayzs.pat.utils.sender.CommandSender;
import java.util.*;
@Plugin(name = "ProAntiTab",
id = "proantitab",
version = "2.5.0",
authors = "Rayzs_YT",
description = "Hides more than just your plugins.",
url = "https://www.rayzs.de/products/proantitab/page",
dependencies = {
@Dependency(id = "luckperms", optional = true),
@Dependency(id = "papiproxybridge", optional = true)
})
public class VelocityLoader implements PluginLoader {
private static VelocityLoader instance;
@@ -91,7 +81,7 @@ public class VelocityLoader implements PluginLoader {
VersionComparer.get().setCurrentVersion(Storage.CURRENT_VERSION);
Storage.loadAll(true);
MessageTranslator.initialize();
MessageHelper.initialize();
GroupManager.initialize();
metricsFactory.make(this, 21638);
@@ -61,7 +61,7 @@ public class BukkitAntiTabListener implements Listener {
return;
}
if (Reflection.isCraftbukkit() && !Bukkit.getOnlinePlayers().contains(player)) {
if (Reflection.isBukkit() && !Bukkit.getOnlinePlayers().contains(player)) {
event.getCommands().clear();
PATScheduler.createScheduler(player::updateCommands, 10);
@@ -20,7 +20,7 @@ import de.rayzs.pat.api.event.events.ExecuteCommandEvent;
import de.rayzs.pat.api.storage.Storage;
import de.rayzs.pat.plugin.logger.Logger;
import de.rayzs.pat.utils.StringUtils;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.utils.permission.PermissionUtil;
public class BukkitBlockCommandListener implements Listener {
@@ -44,7 +44,7 @@ public class BukkitBlockCommandListener implements Listener {
return;
event.setCancelled(true);
MessageTranslator.send(player, Storage.ConfigSections.Settings.CUSTOM_UNKNOWN_COMMAND.MESSAGE, "%command%", command, "%world%", world.getName());
MessageHelper.sendMessage(sender, Storage.ConfigSections.Settings.CUSTOM_UNKNOWN_COMMAND.MESSAGE, "%command%", command, "%world%", world.getName());
}
@EventHandler (priority = EventPriority.LOWEST)
@@ -89,17 +89,16 @@ public class BukkitBlockCommandListener implements Listener {
}
final String displayCommand = StringUtils.replaceTriggers(command, "", "\\", "<", ">", "&");
List<String> notificationMessage = MessageTranslator.replaceMessageList(
Storage.ConfigSections.Messages.NOTIFICATION.ALERT,
final String notificationMessage = StringUtils.replace(
Storage.ConfigSections.Messages.NOTIFICATION.ALERT.getLines(),
"%player%", player.getName(),
"%command%", displayCommand,
"%world%", worldName);
if (Storage.ConfigSections.Settings.CUSTOM_PLUGIN.isCommand(command)) {
MessageTranslator.send(
player,
MessageHelper.sendMessage(
sender,
Storage.ConfigSections.Settings.CUSTOM_PLUGIN.MESSAGE,
"%command%", StringUtils.getFirstArg(displayCommand)
);
@@ -108,9 +107,9 @@ public class BukkitBlockCommandListener implements Listener {
Logger.info(notificationMessage);
Storage.NOTIFY_PLAYERS.forEach(uuid -> {
Object p = Storage.getLoader().getPlayerObjByUUID(uuid);
if (p != null) {
MessageTranslator.send(p, notificationMessage);
final CommandSender otherSender = CommandSender.from(uuid);
if (otherSender != null) {
MessageHelper.sendMessage(otherSender, notificationMessage);
}
});
@@ -120,8 +119,8 @@ public class BukkitBlockCommandListener implements Listener {
if (Storage.ConfigSections.Settings.CUSTOM_VERSION.isCommand(command)) {
MessageTranslator.send(
player,
MessageHelper.sendMessage(
sender,
Storage.ConfigSections.Settings.CUSTOM_VERSION.MESSAGE,
"%command%", StringUtils.getFirstArg(displayCommand)
);
@@ -132,7 +131,7 @@ public class BukkitBlockCommandListener implements Listener {
Storage.NOTIFY_PLAYERS.forEach(uuid -> {
Object p = Storage.getLoader().getPlayerObjByUUID(uuid);
if (p != null) {
MessageTranslator.send(p, notificationMessage);
MessageHelper.sendMessage(sender, notificationMessage);
}
});
@@ -169,7 +168,8 @@ public class BukkitBlockCommandListener implements Listener {
if (executeCommandEvent.isBlocked()) {
event.setCancelled(true);
MessageTranslator.send(
MessageHelper.sendMessage(
sender,
ResponseHandler.getResponse(
sender.getUniqueId(),
@@ -187,8 +187,8 @@ public class BukkitBlockCommandListener implements Listener {
Logger.info(notificationMessage);
Storage.NOTIFY_PLAYERS.stream().filter(uuid -> Bukkit.getServer().getPlayer(uuid) != null).forEach(uuid -> {
Player target = Bukkit.getServer().getPlayer(uuid);
MessageTranslator.send(target, notificationMessage);
final CommandSender otherSender = CommandSender.from(uuid);
MessageHelper.sendMessage(otherSender, notificationMessage);
});
}
@@ -207,7 +207,7 @@ public class BukkitBlockCommandListener implements Listener {
if (executeCommandEvent.isBlocked()) {
event.setCancelled(true);
MessageTranslator.send(
MessageHelper.sendMessage(
sender,
ResponseHandler.getResponse(
sender.getUniqueId(),
@@ -7,7 +7,8 @@ import de.rayzs.pat.plugin.packetanalyzer.bukkit.BukkitPacketAnalyzer;
import de.rayzs.pat.plugin.system.communication.BackendUpdater;
import de.rayzs.pat.plugin.logger.Logger;
import de.rayzs.pat.plugin.system.subargument.SubArgument;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.utils.message.replacer.PlaceholderReplacer;
import de.rayzs.pat.utils.permission.PermissionPlugin;
import de.rayzs.pat.utils.permission.PermissionUtil;
import de.rayzs.pat.api.storage.Storage;
@@ -52,19 +53,23 @@ public class BukkitPlayerListener implements Listener {
}, 20);
}
if (!BukkitPacketAnalyzer.inject(player)) {
final BukkitPacketAnalyzer.InjectionState injectionState = BukkitPacketAnalyzer.inject(player);
if (injectionState == BukkitPacketAnalyzer.InjectionState.FAILED) {
PATScheduler.createScheduler(() -> {
if (player.isOnline()) {
if (!BukkitPacketAnalyzer.inject(player)) {
if (BukkitPacketAnalyzer.inject(player) == BukkitPacketAnalyzer.InjectionState.FAILED) {
if (Storage.ConfigSections.Settings.INJECTION_FAILED.ENABLED) {
Logger.info(MessageTranslator.replaceMessage(
Logger.info(PlaceholderReplacer.applyPlaceholders(
sender,
Storage.ConfigSections.Settings.INJECTION_FAILED.CONSOLE_MESSAGE
));
player.kickPlayer(MessageTranslator.replaceMessage(
player.kickPlayer(PlaceholderReplacer.applyPlaceholders(
sender,
Storage.ConfigSections.Settings.INJECTION_FAILED.KICK_MESSAGE
));
@@ -73,12 +78,14 @@ public class BukkitPlayerListener implements Listener {
}
}
}, 10);
} else if (injectionState == BukkitPacketAnalyzer.InjectionState.SKIPPED) {
Logger.warning("Skipped injection for " + player.getName() + ". Player seems to have a corrupted pipeline. (A fake player?)");
}
if (Storage.OUTDATED && PermissionUtil.hasPermission(sender, "joinupdate", isOperator)) {
PATScheduler.createScheduler(() -> {
if (player.isOnline()) {
MessageTranslator.send(player, Storage.ConfigSections.Settings.UPDATE.OUTDATED.getLines());
MessageHelper.sendMessage(sender, Storage.ConfigSections.Settings.UPDATE.OUTDATED.getLines());
}
}, 20);
}
@@ -7,6 +7,7 @@ import de.rayzs.pat.api.storage.Storage;
import de.rayzs.pat.utils.ExpireCache;
import de.rayzs.pat.utils.StringUtils;
import de.rayzs.pat.utils.message.replacer.PlaceholderReplacer;
import de.rayzs.pat.utils.sender.CommandSender;
import org.bukkit.event.*;
import org.bukkit.Bukkit;
import org.bukkit.profile.PlayerTextures;
@@ -105,7 +106,9 @@ public class PaperServerListPing implements Listener {
.replace("%online%", String.valueOf(online))
.replace("%max%", String.valueOf(max));
return replaced.contains("%") ? PlaceholderReplacer.replace(null, replaced) : replaced;
return replaced.contains("%")
? PlaceholderReplacer.applyPlaceholders(CommandSender.from(Bukkit.getConsoleSender()), replaced)
: replaced;
}
private static class ProtocolHoverLine implements PlayerProfile {
@@ -218,7 +221,7 @@ public class PaperServerListPing implements Listener {
}
@Override
public org.bukkit.profile.@NotNull PlayerProfile clone() {
public PlayerProfile clone() {
return null;
}
@@ -6,7 +6,7 @@ import de.rayzs.pat.plugin.system.subargument.SubArgument;
import de.rayzs.pat.utils.group.Group;
import de.rayzs.pat.utils.group.GroupManager;
import de.rayzs.pat.utils.permission.PermissionUtil;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.utils.response.ResponseHandler;
import de.rayzs.pat.utils.sender.CommandSender;
import net.md_5.bungee.api.event.ChatEvent;
@@ -50,8 +50,8 @@ public class BungeeBlockCommandListener implements Listener {
final String displayCommand = StringUtils.replaceTriggers(command, "", "\\", "<", ">", "&");
final List<String> notificationMessage = MessageTranslator.replaceMessageList(
Storage.ConfigSections.Messages.NOTIFICATION.ALERT,
final String notificationMessage = StringUtils.replace(
Storage.ConfigSections.Messages.NOTIFICATION.ALERT.getLines(),
"%player%", player.getName(),
"%command%", displayCommand,
"%server%", serverName);
@@ -60,8 +60,8 @@ public class BungeeBlockCommandListener implements Listener {
Communicator.Proxy2Backend.sendNotification(player.getUniqueId(), serverName, displayCommand);
MessageTranslator.send(
player,
MessageHelper.sendMessage(
sender,
Storage.ConfigSections.Settings.CUSTOM_PLUGIN.MESSAGE,
"%command%", StringUtils.getFirstArg(displayCommand)
);
@@ -70,9 +70,10 @@ public class BungeeBlockCommandListener implements Listener {
Logger.info(notificationMessage);
Storage.NOTIFY_PLAYERS.stream().forEach(uuid -> {
Object p = Storage.getLoader().getPlayerObjByUUID(uuid);
if (p != null) {
MessageTranslator.send(p, notificationMessage);
final CommandSender otherSender = CommandSender.from(uuid);
if (otherSender != null) {
MessageHelper.sendMessage(otherSender, notificationMessage);
}
});
@@ -84,8 +85,8 @@ public class BungeeBlockCommandListener implements Listener {
Communicator.Proxy2Backend.sendNotification(player.getUniqueId(), serverName, displayCommand);
MessageTranslator.send(
player,
MessageHelper.sendMessage(
sender,
Storage.ConfigSections.Settings.CUSTOM_VERSION.MESSAGE,
"%command%", StringUtils.getFirstArg(displayCommand)
);
@@ -94,9 +95,10 @@ public class BungeeBlockCommandListener implements Listener {
Logger.info(notificationMessage);
Storage.NOTIFY_PLAYERS.forEach(uuid -> {
Object p = Storage.getLoader().getPlayerObjByUUID(uuid);
if (p != null) {
MessageTranslator.send(p, notificationMessage);
final CommandSender otherSender = CommandSender.from(uuid);
if (otherSender != null) {
MessageHelper.sendMessage(otherSender, notificationMessage);
}
});
@@ -133,7 +135,7 @@ public class BungeeBlockCommandListener implements Listener {
if (executeCommandEvent.isBlocked()) {
event.setCancelled(true);
MessageTranslator.send(
MessageHelper.sendMessage(
sender,
ResponseHandler.getResponse(
sender.getUniqueId(),
@@ -153,10 +155,10 @@ public class BungeeBlockCommandListener implements Listener {
Logger.info(notificationMessage);
Storage.NOTIFY_PLAYERS.forEach(uuid -> {
Object p = Storage.getLoader().getPlayerObjByUUID(uuid);
final CommandSender otherSender = CommandSender.from(uuid);
if (p != null) {
MessageTranslator.send(p, notificationMessage);
if (otherSender != null) {
MessageHelper.sendMessage(otherSender, notificationMessage);
}
});
@@ -176,7 +178,7 @@ public class BungeeBlockCommandListener implements Listener {
if (executeCommandEvent.isBlocked()) {
event.setCancelled(true);
MessageTranslator.send(
MessageHelper.sendMessage(
sender,
ResponseHandler.getResponse(
sender.getUniqueId(),
@@ -6,7 +6,7 @@ import de.rayzs.pat.api.event.events.ServerPlayersChangeEvent;
import de.rayzs.pat.plugin.packetanalyzer.proxy.BungeePacketAnalyzer;
import de.rayzs.pat.plugin.system.subargument.SubArgument;
import de.rayzs.pat.utils.permission.PermissionUtil;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.utils.sender.CommandSender;
import net.md_5.bungee.api.connection.ProxiedPlayer;
@@ -39,7 +39,7 @@ public class BungeePlayerConnectionListener implements Listener {
if (Storage.OUTDATED && PermissionUtil.hasPermission(sender, "joinupdate", false)) {
ProxyServer.getInstance().getScheduler().schedule(BungeeLoader.getPlugin(), () -> {
if (player.isConnected()) {
MessageTranslator.send(player, Storage.ConfigSections.Settings.UPDATE.OUTDATED, "%player%", player.getName());
MessageHelper.sendMessage(sender, Storage.ConfigSections.Settings.UPDATE.OUTDATED, "%player%", player.getName());
}
}, 1, TimeUnit.SECONDS);
}
@@ -8,7 +8,7 @@ import de.rayzs.pat.api.event.events.ExecuteCommandEvent;
import de.rayzs.pat.plugin.system.subargument.SubArgument;
import de.rayzs.pat.utils.group.Group;
import de.rayzs.pat.utils.group.GroupManager;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.utils.permission.PermissionUtil;
import com.velocitypowered.api.proxy.Player;
import de.rayzs.pat.api.storage.Storage;
@@ -36,7 +36,7 @@ public class VelocityBlockCommandListener {
}
final CommandSource commandSource = event.getCommandSource();
final Object consoleSender = Storage.getLoader().getConsoleSender();
final CommandSender consoleSender = CommandSender.from(Storage.getLoader().getConsoleSender());
if(! (commandSource instanceof Player))
return event;
@@ -62,8 +62,8 @@ public class VelocityBlockCommandListener {
final String displayCommand = StringUtils.replaceTriggers(command, "", "\\", "<", ">", "&");
final List<String> notificationMessage = MessageTranslator.replaceMessageList(
Storage.ConfigSections.Messages.NOTIFICATION.ALERT,
final String notificationMessage = StringUtils.replace(
Storage.ConfigSections.Messages.NOTIFICATION.ALERT.getLines(),
"%player%", player.getUsername(),
"%command%", displayCommand,
"%server%", serverName
@@ -73,19 +73,20 @@ public class VelocityBlockCommandListener {
Communicator.Proxy2Backend.sendNotification(player.getUniqueId(), serverName, displayCommand);
MessageTranslator.send(
player,
MessageHelper.sendMessage(
sender,
Storage.ConfigSections.Settings.CUSTOM_PLUGIN.MESSAGE,
"%command%", StringUtils.getFirstArg(displayCommand)
);
if (Storage.SEND_CONSOLE_NOTIFICATION)
MessageTranslator.send(consoleSender, notificationMessage);
MessageHelper.sendMessage(consoleSender, notificationMessage);
Storage.NOTIFY_PLAYERS.forEach(uuid -> {
Object p = Storage.getLoader().getPlayerObjByUUID(uuid);
if (p != null) {
MessageTranslator.send(p, notificationMessage);
final CommandSender otherSender = CommandSender.from(uuid);
if (otherSender != null) {
MessageHelper.sendMessage(otherSender, notificationMessage);
}
});
@@ -97,19 +98,20 @@ public class VelocityBlockCommandListener {
Communicator.Proxy2Backend.sendNotification(player.getUniqueId(), serverName, displayCommand);
MessageTranslator.send(
player,
MessageHelper.sendMessage(
sender,
Storage.ConfigSections.Settings.CUSTOM_VERSION.MESSAGE,
"%command%", StringUtils.getFirstArg(displayCommand)
);
if (Storage.SEND_CONSOLE_NOTIFICATION)
MessageTranslator.send(consoleSender, notificationMessage);
MessageHelper.sendMessage(consoleSender, notificationMessage);
Storage.NOTIFY_PLAYERS.forEach(uuid -> {
Object p = Storage.getLoader().getPlayerObjByUUID(uuid);
if (p != null) {
MessageTranslator.send(p, notificationMessage);
final CommandSender otherSender = CommandSender.from(uuid);
if (otherSender != null) {
MessageHelper.sendMessage(otherSender, notificationMessage);
}
});
@@ -139,7 +141,6 @@ public class VelocityBlockCommandListener {
if (!allowed) {
ExecuteCommandEvent executeCommandEvent = PATEventHandler.callExecuteCommandEvents(
sender,
event.getCommand(),
@@ -150,7 +151,7 @@ public class VelocityBlockCommandListener {
if (executeCommandEvent.isBlocked()) {
event.setResult(CommandExecuteEvent.CommandResult.denied());
MessageTranslator.send(
MessageHelper.sendMessage(
sender,
ResponseHandler.getResponse(
sender.getUniqueId(),
@@ -167,12 +168,13 @@ public class VelocityBlockCommandListener {
Communicator.Proxy2Backend.sendNotification(player.getUniqueId(), serverName, displayCommand);
if (Storage.SEND_CONSOLE_NOTIFICATION)
MessageTranslator.send(consoleSender, notificationMessage);
MessageHelper.sendMessage(consoleSender, notificationMessage);
Storage.NOTIFY_PLAYERS.forEach(uuid -> {
Object p = Storage.getLoader().getPlayerObjByUUID(uuid);
if (p != null) {
MessageTranslator.send(p, notificationMessage);
final CommandSender otherSender = CommandSender.from(uuid);
if (otherSender != null) {
MessageHelper.sendMessage(otherSender, notificationMessage);
}
});
}
@@ -190,9 +192,25 @@ public class VelocityBlockCommandListener {
);
if (executeCommandEvent.isBlocked()) {
/*
if (event.getCommand().toLowerCase().contains("discord")) {
Logger.info("Blocked for " + player.getUsername() + " (" + command + " -> " + event.getCommand() + ")");
Logger.info("Groups: " + (groups.isEmpty() ? "None" : String.join(", ", groups.stream().map(Group::getGroupName).toList())));
Logger.info("Server: " + serverName + " (" + sender.getServerName() + ")");
Arguments argument = SubArgument.get().getPlayerArgument(sender);
if (argument != null) {
Logger.info("Chat Arguments: " + String.join(", ", argument.CHAT_ARGUMENTS.getAllInputs()));
Logger.info("Tab Arguments: " + String.join(", ", argument.TAB_ARGUMENTS.getAllInputs()));
} else Logger.info("Arguments: /");
}
*/
event.setResult(CommandExecuteEvent.CommandResult.denied());
MessageTranslator.send(
MessageHelper.sendMessage(
sender,
ResponseHandler.getResponse(
sender.getUniqueId(),
@@ -8,7 +8,8 @@ import de.rayzs.pat.api.event.events.ServerPlayersChangeEvent;
import de.rayzs.pat.plugin.packetanalyzer.proxy.VelocityPacketAnalyzer;
import de.rayzs.pat.plugin.logger.Logger;
import de.rayzs.pat.plugin.system.subargument.SubArgument;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.utils.message.replacer.PlaceholderReplacer;
import de.rayzs.pat.utils.permission.PermissionUtil;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.player.*;
@@ -55,7 +56,7 @@ public class VelocityConnectionListener {
if (Storage.OUTDATED && PermissionUtil.hasPermission(sender, "joinupdate", false)) {
server.getScheduler().buildTask(loader, () -> {
if (player.isActive())
MessageTranslator.send(player, Storage.ConfigSections.Settings.UPDATE.OUTDATED, "%player%", player.getUsername());
MessageHelper.sendMessage(sender, Storage.ConfigSections.Settings.UPDATE.OUTDATED, "%player%", player.getUsername());
}).delay(1, TimeUnit.SECONDS).schedule();
}
}
@@ -63,6 +64,7 @@ public class VelocityConnectionListener {
@Subscribe
public void onServerSwitch(ServerConnectedEvent event) {
final Player player = event.getPlayer();
final CommandSender sender = CommandSender.from(player);
if (!VelocityPacketAnalyzer.isInjected(player)) {
@@ -71,14 +73,14 @@ public class VelocityConnectionListener {
if (!VelocityPacketAnalyzer.inject(player)) {
if (Storage.ConfigSections.Settings.INJECTION_FAILED.ENABLED) {
Logger.info(MessageTranslator.replaceMessage(
player,
Logger.info(PlaceholderReplacer.applyPlaceholders(
sender,
Storage.ConfigSections.Settings.INJECTION_FAILED.CONSOLE_MESSAGE
));
player.disconnect(Component.text(
MessageTranslator.replaceMessage(
player,
PlaceholderReplacer.applyPlaceholders(
sender,
Storage.ConfigSections.Settings.INJECTION_FAILED.KICK_MESSAGE
)
));
@@ -93,7 +95,6 @@ public class VelocityConnectionListener {
final ServerInfo serverInfo = event.getServer().getServerInfo();
if (serverInfo != null) Storage.tempCachePlayerToServer(player.getUniqueId(), serverInfo.getName());
CommandSender sender = CommandSender.from(player);
Objects.requireNonNull(sender);
PermissionUtil.reloadPermissions(sender);
}
@@ -1,13 +1,16 @@
package de.rayzs.pat.plugin.logger.impl;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.plugin.BukkitLoader;
import de.rayzs.pat.plugin.logger.*;
import de.rayzs.pat.utils.message.replacer.PlaceholderReplacer;
import de.rayzs.pat.utils.sender.CommandSender;
import org.bukkit.Bukkit;
import java.util.*;
public class BukkitLogger implements LoggerTemplate {
private final CommandSender console = CommandSender.from(Bukkit.getServer().getConsoleSender());
private final java.util.logging.Logger logger = BukkitLoader.getPluginLogger();
@Override
@@ -37,10 +40,10 @@ public class BukkitLogger implements LoggerTemplate {
@Override
public void send(LoggerPriority priority, String message) {
message = MessageTranslator.replaceMessage(message);
message = PlaceholderReplacer.applyPlaceholders(null, message);
if (message.contains("§")) {
MessageTranslator.send(Bukkit.getServer().getConsoleSender(), message);
MessageHelper.sendMessage(console, message);
return;
}
@@ -1,13 +1,17 @@
package de.rayzs.pat.plugin.logger.impl;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.plugin.BungeeLoader;
import de.rayzs.pat.utils.message.replacer.PlaceholderReplacer;
import de.rayzs.pat.utils.sender.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import de.rayzs.pat.plugin.logger.*;
import java.util.*;
public class BungeeLogger implements LoggerTemplate {
private final CommandSender console = CommandSender.from(ProxyServer.getInstance().getConsole());
private final java.util.logging.Logger logger = BungeeLoader.getPluginLogger();
@Override
@@ -37,10 +41,10 @@ public class BungeeLogger implements LoggerTemplate {
@Override
public void send(LoggerPriority priority, String message) {
message = MessageTranslator.replaceMessage(message);
message = PlaceholderReplacer.applyPlaceholders(null, message);
if (message.contains("§")) {
MessageTranslator.send(ProxyServer.getInstance().getConsole(), message);
MessageHelper.sendMessage(console, message);
return;
}
@@ -1,13 +1,16 @@
package de.rayzs.pat.plugin.logger.impl;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.plugin.VelocityLoader;
import de.rayzs.pat.plugin.logger.*;
import de.rayzs.pat.utils.message.replacer.PlaceholderReplacer;
import de.rayzs.pat.utils.sender.CommandSender;
import org.slf4j.Logger;
import java.util.*;
public class VelocityLogger implements LoggerTemplate {
private final CommandSender console = CommandSender.from(VelocityLoader.getServer().getConsoleCommandSource());
private final Logger logger = VelocityLoader.getPluginLogger();
@Override
@@ -37,10 +40,10 @@ public class VelocityLogger implements LoggerTemplate {
@Override
public void send(LoggerPriority priority, String message) {
message = MessageTranslator.replaceMessage(message);
message = PlaceholderReplacer.applyPlaceholders(null, message);
if (message.contains("§")) {
MessageTranslator.send(VelocityLoader.getServer().getConsoleCommandSource(), message);
MessageHelper.sendMessage(console, message);
return;
}
@@ -1,6 +1,5 @@
package de.rayzs.pat.plugin.packetanalyzer.bukkit;
import com.mojang.datafixers.kinds.Const;
import de.rayzs.pat.plugin.logger.Logger;
import de.rayzs.pat.plugin.packetanalyzer.bukkit.handlers.LegacyPacketHandler;
import de.rayzs.pat.plugin.packetanalyzer.bukkit.handlers.ModernCommandsNodeHandler;
@@ -54,15 +53,15 @@ public class BukkitPacketAnalyzer {
channel.pipeline().writeAndFlush(object);
}
public static boolean inject(Player player) {
public static InjectionState inject(Player player) {
if (Storage.ConfigSections.Settings.HANDLE_THROUGH_PROXY.ENABLED && !Storage.ConfigSections.Settings.HIDE_PLUGIN_CHANNELS.ENABLED)
return true;
return InjectionState.SKIPPED;
try {
Channel channel = Reflection.getPlayerChannel(player);
if (channel == null) {
return false;
if (channel == null || !channel.pipeline().names().contains(PIPELINE_NAME)) {
return InjectionState.SKIPPED;
}
if (channel.pipeline().names().contains(BukkitPacketAnalyzer.HANDLER_NAME))
@@ -78,31 +77,36 @@ public class BukkitPacketAnalyzer {
// changed the compilation and therefore the variables again.
Reflection.toggleInjectionMethod();
final boolean success = inject(player);
final InjectionState state = inject(player);
if (!success) {
if (state == InjectionState.FAILED) {
Logger.warning("Both injection methods failed! Please report this back to me on my Discord! (https://www.rayzs.de/discord)");
}
return success;
return state;
} catch (NoSuchFieldException noSuchFieldException) {
if (!Storage.ConfigSections.Settings.INJECTION_FAILED.ENABLED) {
return InjectionState.SUCCESS;
}
Logger.warning("Failed to inject into " + player.getName() + " and kicked the player as result, to avoid any security risks.");
Logger.warning("You can read more about it here: https://www.rayzs.de/products/proantitab/pkafi");
Logger.warning("Error details: " + noSuchFieldException.getMessage());
return false;
return InjectionState.FAILED;
} catch (Exception exception) {
if (!Storage.ConfigSections.Settings.INJECTION_FAILED.SUPPRESS_EXCEPTIONS) {
exception.printStackTrace();
}
return false;
return InjectionState.FAILED;
}
return true;
return InjectionState.SUCCESS;
}
public static void uninject(UUID uuid) {
@@ -129,6 +133,10 @@ public class BukkitPacketAnalyzer {
}
}
public enum InjectionState {
SUCCESS, FAILED, SKIPPED
}
public static String getPlayerInput(Player player) {
String input = PLAYER_INPUT_CACHE.get(player);
PLAYER_INPUT_CACHE.remove(player);
@@ -281,7 +289,14 @@ public class BukkitPacketAnalyzer {
payloadField.setAccessible(true);
final Object payloadObj = payloadField.get(packet);
final Field channelField = payloadObj.getClass().getDeclaredField("id");
Field channelField;
try {
channelField = payloadObj.getClass().getDeclaredField("id");
} catch (NoSuchFieldException noSuchFieldException) {
return packet;
}
channelField.setAccessible(true);
final Object channelIdObj = channelField.get(payloadObj);
@@ -23,7 +23,7 @@ import de.rayzs.pat.api.storage.Storage;
import de.rayzs.pat.plugin.logger.Logger;
import de.rayzs.pat.utils.Reflection;
import de.rayzs.pat.utils.StringUtils;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.utils.scheduler.PATScheduler;
public class ModernPacketHandler implements BukkitPacketHandler {
@@ -48,8 +48,8 @@ public class ModernPacketHandler implements BukkitPacketHandler {
if (!text.isBlank() && text.charAt(0) != '/') text = '/' + text;
if (Storage.ConfigSections.Settings.PATCH_EXPLOITS.isMalicious(text)) {
MessageTranslator.send(
Bukkit.getConsoleSender(),
MessageHelper.sendMessage(
CommandSender.from(Bukkit.getConsoleSender()),
Storage.ConfigSections.Settings.PATCH_EXPLOITS.ALERT_MESSAGE.get().replace("%player%", player.getName())
);
@@ -23,8 +23,6 @@ import de.rayzs.pat.utils.node.ProxyCommandNodeHelper;
import de.rayzs.pat.utils.Reflection;
import de.rayzs.pat.utils.permission.PermissionUtil;
import de.rayzs.pat.utils.sender.CommandSender;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
@@ -150,16 +148,17 @@ public class BungeePacketAnalyzer {
final boolean ignore = PermissionUtil.hasBypassPermission(sender, false) || Storage.Blacklist.isDisabledServer(serverName);
ProxyCommandNodeHelper helper = new ProxyCommandNodeHelper<CommandNode>(commands.getRoot());
List<String> commandsAsString = new ArrayList<>(PROXY_COMMANDS);
final ProxyCommandNodeHelper helper = new ProxyCommandNodeHelper<CommandNode>(commands.getRoot());
final List<String> commandsAsString = new ArrayList<>(PROXY_COMMANDS);
commandsAsString.addAll(helper.getChildrenNames());
final List<Group> groups = ignore
? Collections.emptyList()
: GroupManager.getPlayerGroups(sender, false);
HashSet<String> playerCommands = new HashSet<>();
if (!ignore) {
final List<Group> groups = GroupManager.getPlayerGroups(sender, false);
final Map<String, CommandsCache> cache = Storage.getLoader().getPerServerCommandsCacheMap();
if (!cache.containsKey(serverName)) {
cache.put(serverName, new CommandsCache());
@@ -220,6 +219,14 @@ public class BungeePacketAnalyzer {
}
}
if (Storage.ConfigSections.Settings.TAB_COMPLETION_FOR_NOT_EXISTING_COMMANDS.ENABLED) {
for (Group group : groups) {
for (String groupCommands : group.getAllCommands(serverName)) {
helper.add(groupCommands, true);
}
}
}
if (!ignore) {
SubArgument.get().getCommandNodeHandler().handleCommandNode(helper, SubArgument.get().getPlayerArgument(sender));
}
@@ -27,7 +27,7 @@ import de.rayzs.pat.utils.Reflection;
import de.rayzs.pat.utils.StringUtils;
import de.rayzs.pat.utils.group.Group;
import de.rayzs.pat.utils.group.GroupManager;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.utils.node.ProxyCommandNodeHelper;
import de.rayzs.pat.utils.permission.PermissionUtil;
import de.rayzs.pat.utils.sender.CommandSender;
@@ -207,6 +207,18 @@ public class VelocityPacketAnalyzer {
return !playerCommands.contains(str);
});
if (Storage.ConfigSections.Settings.TAB_COMPLETION_FOR_NOT_EXISTING_COMMANDS.ENABLED) {
for (Group group : groups) {
for (String groupServerName : group.getBlacklistServerNames(serverName)) {
if (Storage.isServer(groupServerName, serverName)) {
for (String groupCommands : group.getAllCommands(groupServerName)) {
helper.add(groupCommands, true);
}
}
}
}
}
if (Storage.ConfigSections.Settings.CUSTOM_VERSION.ALWAYS_TAB_COMPLETABLE) {
Storage.ConfigSections.Settings.CUSTOM_VERSION.COMMANDS.getLines().forEach(input -> helper.add(input, false));
}
@@ -298,7 +310,7 @@ public class VelocityPacketAnalyzer {
if (request.getCommand() != null) {
if (Storage.ConfigSections.Settings.PATCH_EXPLOITS.isMalicious(request.getCommand())) {
MessageTranslator.send(VelocityLoader.getServer().getConsoleCommandSource(), Storage.ConfigSections.Settings.PATCH_EXPLOITS.ALERT_MESSAGE.get().replace("%player%", player.getUsername()));
MessageHelper.sendMessage(CommandSender.from(VelocityLoader.getServer().getConsoleCommandSource()), Storage.ConfigSections.Settings.PATCH_EXPLOITS.ALERT_MESSAGE.get().replace("%player%", player.getUsername()));
player.disconnect(LegacyComponentSerializer.legacyAmpersand().deserialize(Storage.ConfigSections.Settings.PATCH_EXPLOITS.KICK_MESSAGE.get()));
} else {
insertPlayerInput(player, request.getCommand());
@@ -186,10 +186,6 @@ public class Communicator {
// BACKEND -> PROXY
public void handleB2PPacket(String serverName, CommunicationPackets.PATPacket incomingPacket) {
if (!incomingPacket.tokenMatches(Storage.TOKEN)) {
return;
}
if (incomingPacket instanceof CommunicationPackets.Backend2Proxy.KeepAlivePacket packet) {
final Client client = clients.get(serverName);
if (client == null) return;
@@ -253,10 +249,6 @@ public class Communicator {
// PROXY -> BACKEND
public void handleP2BPacket(CommunicationPackets.PATPacket incomingPacket) {
if (!incomingPacket.tokenMatches(Storage.TOKEN)) {
return;
}
if (incomingPacket instanceof CommunicationPackets.Proxy2Backend.KeepAliveResponsePacket packet) {
lastReceivedKeepAliveResponse = System.currentTimeMillis();
@@ -6,7 +6,8 @@ import de.rayzs.pat.plugin.system.communication.cph.CommunicationPacketHandler;
import de.rayzs.pat.plugin.logger.Logger;
import de.rayzs.pat.utils.CommunicationPackets;
import de.rayzs.pat.utils.Reflection;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.StringUtils;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.utils.scheduler.PATScheduler;
import de.rayzs.pat.utils.sender.CommandSender;
import org.bukkit.Bukkit;
@@ -59,12 +60,13 @@ public class BukkitCommunicationHandler implements CommunicationPacketHandler {
return;
}
final List<String> notificationMessage = MessageTranslator.replaceMessageList(
Storage.ConfigSections.Messages.NOTIFICATION.ALERT,
final String notificationMessage = StringUtils.replace(
Storage.ConfigSections.Messages.NOTIFICATION.ALERT.getLines(),
"%player%", player.getName(),
"%command%", packet.command(),
"%server%", Storage.SERVER_NAME,
"%world%", player.getWorld().getName());
"%world%", player.getWorld().getName()
);
Logger.info(notificationMessage);
}
@@ -80,7 +80,12 @@ public class BungeePluginMessageClient implements PluginMessageClient, Listener
return;
}
final Server server = (Server) event.getSender();
// Private channel: never forward matching traffic between a player and backend.
event.setCancelled(true);
if (!(event.getSender() instanceof Server server)) {
return;
}
final String serverName = server.getInfo().getName();
final UUID clientId = Communicator.get().getClientId(serverName);
@@ -76,11 +76,17 @@ public class VelocityPluginMessageClient implements PluginMessageClient {
@Subscribe
public void onQueryReceive(PluginMessageEvent event) {
if (event.getIdentifier() != IDENTIFIER) {
if (!IDENTIFIER.equals(event.getIdentifier())) {
return;
}
final ServerConnection server = (ServerConnection) event.getSource();
// This is a private proxy/backend channel. Mark it handled before inspecting
// the source so player-originated messages are never forwarded downstream.
event.setResult(PluginMessageEvent.ForwardResult.handled());
if (!(event.getSource() instanceof ServerConnection server)) {
return;
}
final String serverName = server.getServerInfo().getName();
final UUID clientId = Communicator.get().getClientId(serverName);
@@ -2,12 +2,15 @@ package de.rayzs.pat.plugin.system.serverbrand.impl;
import de.rayzs.pat.plugin.packetanalyzer.bukkit.BukkitPacketAnalyzer;
import de.rayzs.pat.plugin.system.serverbrand.ServerBrand;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.api.storage.Storage;
import de.rayzs.pat.plugin.BukkitLoader;
import de.rayzs.pat.utils.message.replacer.PlaceholderReplacer;
import de.rayzs.pat.utils.scheduler.PATScheduler;
import de.rayzs.pat.utils.scheduler.PATSchedulerTask;
import de.rayzs.pat.utils.sender.CommandSender;
import io.netty.channel.Channel;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import de.rayzs.pat.utils.*;
@@ -137,7 +140,7 @@ public class BukkitServerBrand implements ServerBrand {
if (!Reflection.isWeird()) {
final PacketUtils.BrandManipulate serverBrand = new PacketUtils.BrandManipulate(
MessageTranslator.replaceMessage(player, customBrand)
PlaceholderReplacer.applyPlaceholders(CommandSender.from(player), customBrand)
);
player.sendPluginMessage(
@@ -158,7 +161,7 @@ public class BukkitServerBrand implements ServerBrand {
final Object brandPayloadObj = brandPayloadClass
.getDeclaredConstructor(String.class)
.newInstance(MessageTranslator.replaceMessage(player, customBrand));
.newInstance(PlaceholderReplacer.applyPlaceholders(CommandSender.from(player), customBrand));
final Object customPacketPayloadPacket = clientBoundCustomPacketPayloadPacketClass
.getDeclaredConstructor(customPacketPayloadPacketClass)
@@ -2,7 +2,8 @@ package de.rayzs.pat.plugin.system.serverbrand.impl;
import de.rayzs.pat.plugin.system.serverbrand.ServerBrand;
import de.rayzs.pat.utils.StringUtils;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.utils.message.replacer.PlaceholderReplacer;
import net.md_5.bungee.api.scheduler.ScheduledTask;
import net.md_5.bungee.protocol.ProtocolConstants;
import net.md_5.bungee.api.connection.*;
@@ -29,7 +30,7 @@ public class BungeeServerBrand implements ServerBrand {
}
if (Storage.ConfigSections.Settings.CUSTOM_BRAND.REPEAT_DELAY == -1) {
BRAND = MessageTranslator.replaceMessage(
BRAND = PlaceholderReplacer.applyPlaceholders(null,
Storage.ConfigSections.Settings.CUSTOM_BRAND.BRANDS.getLines().get(0)
) + "§r";
@@ -46,7 +47,7 @@ public class BungeeServerBrand implements ServerBrand {
public void run() {
animationPos = (++animationPos) % length;
BRAND = MessageTranslator.replaceMessage(
BRAND = PlaceholderReplacer.applyPlaceholders(null,
Storage.ConfigSections.Settings.CUSTOM_BRAND.BRANDS.getLines().get(animationPos)
) + "§r";
@@ -2,7 +2,8 @@ package de.rayzs.pat.plugin.system.serverbrand.impl;
import com.velocitypowered.api.scheduler.ScheduledTask;
import de.rayzs.pat.plugin.system.serverbrand.ServerBrand;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.utils.message.replacer.PlaceholderReplacer;
import net.md_5.bungee.protocol.ProtocolConstants;
import de.rayzs.pat.plugin.VelocityLoader;
import de.rayzs.pat.api.storage.Storage;
@@ -49,7 +50,7 @@ public class VelocityServerBrand implements ServerBrand {
}
if (Storage.ConfigSections.Settings.CUSTOM_BRAND.REPEAT_DELAY == -1) {
BRAND = MessageTranslator.replaceMessage(Storage.ConfigSections.Settings.CUSTOM_BRAND.BRANDS.getLines().get(0)) + "§r";
BRAND = PlaceholderReplacer.applyPlaceholders(null, Storage.ConfigSections.Settings.CUSTOM_BRAND.BRANDS.getLines().get(0)) + "§r";
SERVER.getAllPlayers().forEach(this::send);
} else {
TASK = SERVER.getScheduler().buildTask(VelocityLoader.getInstance(), new Runnable() {
@@ -61,7 +62,7 @@ public class VelocityServerBrand implements ServerBrand {
public void run() {
animationPos = (++animationPos) % length;
BRAND = MessageTranslator.replaceMessage(
BRAND = PlaceholderReplacer.applyPlaceholders(null,
Storage.ConfigSections.Settings.CUSTOM_BRAND.BRANDS.getLines().get(animationPos)
) + "§r";
@@ -98,6 +98,8 @@ public class CommandNodeHandler extends SubArgumentHandler {
break;
}
if (input == null) continue;
final Storage.Blacklist.BlockType type = Storage.Blacklist.BlockTypeFetcher.getType(input);
if (type == Storage.Blacklist.BlockType.CHAT) {
continue;
@@ -1,6 +1,7 @@
package de.rayzs.pat.plugin.system.subargument.handler.impl;
import de.rayzs.pat.api.storage.Storage;
import de.rayzs.pat.plugin.logger.Logger;
import de.rayzs.pat.plugin.system.subargument.handler.SubArgumentHandler;
import de.rayzs.pat.plugin.system.subargument.SubArgument;
import de.rayzs.pat.utils.StringUtils;
@@ -67,6 +68,16 @@ public class ExecuteHandler extends SubArgumentHandler {
listed = !turn;
}
/*
final boolean blocked = turn != listed;
if (blocked && command.contains("discord")) {
Logger.info("Yes, sub-argument was blocked! (" + sender.getName() + ", " + command + ")");
Logger.info("-> " + turn + ", " + listed + ", " + negated);
}
return blocked;
*/
return turn != listed;
}
@@ -116,7 +116,7 @@ public class CommandsCache {
String command = filteredCommands.get(i);
localFilteredCommands.add(command);
} catch (IndexOutOfBoundsException indexOutOfBoundsException) {
Logger.warning("Array is out of bounds " + i + "/" + max + "! " + indexOutOfBoundsException.getMessage());
//Logger.warning("Array is out of bounds " + i + "/" + max + "! " + indexOutOfBoundsException.getMessage());
break;
}
}
@@ -1,50 +1,93 @@
package de.rayzs.pat.utils;
import de.rayzs.pat.plugin.system.communication.Communicator;
import de.rayzs.pat.api.storage.Storage;
import de.rayzs.pat.plugin.system.communication.Communicator;
import de.rayzs.pat.utils.configuration.helper.MultipleMessagesHelper;
import java.util.*;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.*;
/**
* Wire codec for communication between proxy and backend servers.
*
* <p>Packets are explicitly encoded and protected with AES-256-GCM before
* transmission. Encryption keys are derived from {@link Storage#TOKEN},
* using backend UUIDs only as derivation context.
*
* <p>The codec does not use native Java deserialization for network data,
* and all variable-length fields are bounded before allocation.
*/
public class CommunicationPackets {
public static final byte PROTOCOL_VERSION = 1;
private static final byte INIT_PACKET = 0;
private static final byte NOT_INIT_PACKET = 1;
private static final int HEADER_LENGTH = 2;
private static final int NONCE_LENGTH = 12;
private static final int GCM_TAG_BITS = 128;
private static final int GCM_TAG_LENGTH = GCM_TAG_BITS / 8;
// Velocity defaults to 32,767 bytes for plugin message payloads.
private static final int MAX_WIRE_BYTES = 32_767;
private static final int MAX_PLAINTEXT_BYTES = MAX_WIRE_BYTES - HEADER_LENGTH - NONCE_LENGTH - GCM_TAG_LENGTH;
private static final SecureRandom RANDOM = new SecureRandom();
private static final byte[] HKDF_SALT = "ProAntiTab/plugin-messaging/v1".getBytes(StandardCharsets.UTF_8);
// Stable packet IDs. Do not reorder/reuse IDs without a protocol-version bump.
private static final byte P_UPDATE = 1;
private static final byte P_IDENTITY = 2;
private static final byte P_NOTIFICATION = 3;
private static final byte P_EXEC_CONSOLE = 4;
private static final byte P_EXEC_PLAYER = 5;
private static final byte P_CONSOLE_MESSAGE = 6;
private static final byte P_KEEPALIVE_RESPONSE = 7;
private static final byte P_DATA_SYNC = 8;
private static final byte P_IDENTITY_REQUEST = 20;
private static final byte P_IDENTITY_RESPONSE = 21;
private static final byte P_DATA_SYNC_RECEIVED = 22;
private static final byte P_KEEPALIVE = 23;
private static final byte P_PLAYER_PERMISSION_CHANGE = 24;
private static final byte P_GROUP_PERMISSION_CHANGE = 25;
public static byte[] preparePacket(Object object) {
return preparePacket(object, Communicator.get().getId());
}
public static byte[] preparePacket(Object object, UUID id) {
if (!isValidPacket(object)) {
if (!isValidPacket(object) || Storage.TOKEN == null || Storage.TOKEN.isBlank()) {
return null;
}
final boolean isInitialPacket = isInitialPacket(object);
final ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
try {
final boolean initial = isInitialPacket(object);
if (!initial && id == null) {
return null;
}
final byte[] plaintext = encodePacket((PATPacket) object);
final byte[] nonce = new byte[NONCE_LENGTH];
RANDOM.nextBytes(nonce);
try (ObjectOutputStream outputStream = new ObjectOutputStream(arrayOutputStream)) {
outputStream.writeObject(object);
outputStream.flush();
// Wire format: [version:1][init:1][nonce:12][ciphertext + GCM tag:16].
// The version and init flag are authenticated as GCM additional authenticated data.
final byte[] header = new byte[]{PROTOCOL_VERSION, initial ? INIT_PACKET : NOT_INIT_PACKET};
final SecretKeySpec key = deriveKey(Storage.TOKEN, initial ? null : id);
final byte[] ciphertext = encrypt(plaintext, key, nonce, header);
final byte[] encryptedBytes = xor(arrayOutputStream.toByteArray(), isInitialPacket
? Storage.TOKEN
: id.toString()
);
final byte[] bytes = new byte[encryptedBytes.length + 1];
bytes[0] = isInitialPacket ? INIT_PACKET : NOT_INIT_PACKET;
System.arraycopy(encryptedBytes, 0, bytes, 1, bytes.length - 1);
return bytes;
} catch (Exception ignored) { }
return null;
final byte[] result = new byte[header.length + NONCE_LENGTH + ciphertext.length];
System.arraycopy(header, 0, result, 0, header.length);
System.arraycopy(nonce, 0, result, header.length, NONCE_LENGTH);
System.arraycopy(ciphertext, 0, result, header.length + NONCE_LENGTH, ciphertext.length);
return result;
} catch (IOException | GeneralSecurityException ignored) {
return null;
}
}
public static Object readPacket(byte[] bytes) {
@@ -52,147 +95,265 @@ public class CommunicationPackets {
}
public static Object readPacket(byte[] bytes, UUID id) {
if (bytes.length == 0) {
if (bytes == null || bytes.length < HEADER_LENGTH + NONCE_LENGTH + GCM_TAG_LENGTH
|| bytes.length > MAX_WIRE_BYTES || Storage.TOKEN == null || Storage.TOKEN.isBlank()) {
return null;
}
final byte init = bytes[0];
bytes = Arrays.copyOfRange(bytes, 1, bytes.length);
bytes = xor(bytes, isInitialPacket(init)
? Storage.TOKEN
: id.toString()
);
final ByteArrayInputStream arrayInputStream = new ByteArrayInputStream(bytes);
try (ObjectInput input = new ObjectInputStream(arrayInputStream)) {
final Object object = input.readObject();
if (object.getClass() == null || !isValidPacket(object)) {
try {
final byte version = bytes[0];
final byte initFlag = bytes[1];
if (version != PROTOCOL_VERSION || (initFlag != INIT_PACKET && initFlag != NOT_INIT_PACKET)) {
return null;
}
return object;
} catch (Exception ignored) { }
final boolean initial = initFlag == INIT_PACKET;
if (!initial && id == null) {
return null;
}
return null;
final byte[] header = Arrays.copyOfRange(bytes, 0, 2);
final byte[] nonce = Arrays.copyOfRange(bytes, 2, 2 + NONCE_LENGTH);
final byte[] ciphertext = Arrays.copyOfRange(bytes, 2 + NONCE_LENGTH, bytes.length);
final SecretKeySpec key = deriveKey(Storage.TOKEN, initial ? null : id);
// Authentication is verified by GCM before decodePacket() sees any plaintext.
final byte[] plaintext = decrypt(ciphertext, key, nonce, header);
return decodePacket(plaintext, initial);
} catch (IOException | GeneralSecurityException ignored) {
return null;
}
}
// Yes, I am aware that xor is not the best encryption out there,
// but it will suffice for now since the encryption shouldn't be too heavy.
private static byte[] xor(byte[] bytes, String key) {
final byte[] output = new byte[bytes.length];
final byte[] secret = key.getBytes();
private static SecretKeySpec deriveKey(String token, UUID id) throws GeneralSecurityException {
// RFC 5869 HKDF-SHA-256. The token is the input keying material; the UUID is
// public context used to separate normal-session keys between backend identities.
final Mac mac = Mac.getInstance("HmacSHA256");
int pos = 0;
// HKDF-Extract(salt, IKM)
mac.init(new SecretKeySpec(HKDF_SALT, "HmacSHA256"));
final byte[] prk = mac.doFinal(token.getBytes(StandardCharsets.UTF_8));
for (int i = 0; i < bytes.length; i++) {
output[i] = (byte) (bytes[i] ^ secret[pos]);
pos += 1;
// HKDF-Expand(PRK, info, 32). One HMAC block is sufficient for an AES-256 key.
final String context = id == null ? "initial" : "backend:" + id;
final byte[] info = context.getBytes(StandardCharsets.UTF_8);
mac.init(new SecretKeySpec(prk, "HmacSHA256"));
mac.update(info);
mac.update((byte) 0x01);
final byte[] okm = mac.doFinal();
return new SecretKeySpec(okm, "AES");
}
if (pos >= secret.length) {
pos = 0;
private static byte[] encrypt(byte[] plaintext, SecretKeySpec key, byte[] nonce, byte[] aad) throws GeneralSecurityException {
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(GCM_TAG_BITS, nonce));
cipher.updateAAD(aad);
return cipher.doFinal(plaintext);
}
private static byte[] decrypt(byte[] ciphertext, SecretKeySpec key, byte[] nonce, byte[] aad) throws GeneralSecurityException {
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(GCM_TAG_BITS, nonce));
cipher.updateAAD(aad);
return cipher.doFinal(ciphertext);
}
private static byte[] encodePacket(PATPacket packet) throws IOException {
final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try (DataOutputStream out = new DataOutputStream(new BoundedOutputStream(buffer, MAX_PLAINTEXT_BYTES))) {
if (packet instanceof Proxy2Backend.UpdatePacket p) {
out.writeByte(P_UPDATE); writeNullableUuid(out, p.playerId());
} else if (packet instanceof Proxy2Backend.IdentityPacket p) {
out.writeByte(P_IDENTITY); writeUuid(out, p.targetServerId()); writeString(out, p.serverName());
} else if (packet instanceof Proxy2Backend.NotificationPacket p) {
out.writeByte(P_NOTIFICATION); writeUuid(out, p.playerId()); writeString(out, p.command());
} else if (packet instanceof Proxy2Backend.ExecuteConsoleCommandPacket p) {
out.writeByte(P_EXEC_CONSOLE); writeString(out, p.command());
} else if (packet instanceof Proxy2Backend.ExecutePlayerCommandPacket p) {
out.writeByte(P_EXEC_PLAYER); writeUuid(out, p.playerId()); writeString(out, p.command());
} else if (packet instanceof Proxy2Backend.ConsoleMessagePacket p) {
out.writeByte(P_CONSOLE_MESSAGE); writeString(out, p.message());
} else if (packet instanceof Proxy2Backend.KeepAliveResponsePacket) {
out.writeByte(P_KEEPALIVE_RESPONSE);
} else if (packet instanceof Proxy2Backend.DataSyncPacket p) {
out.writeByte(P_DATA_SYNC);
writeString(out, p.messages().prefix());
out.writeBoolean(p.autoLowerCase().enabled());
out.writeBoolean(p.unknownCommand().enabled());
final List<String> lines = p.unknownCommand().message().getLines();
out.writeInt(lines.size());
for (String line : lines) writeString(out, line);
} else if (packet instanceof Backend2Proxy.IdentityRequestPacket p) {
out.writeByte(P_IDENTITY_REQUEST); writeUuid(out, p.serverId());
} else if (packet instanceof Backend2Proxy.IdentityResponsePacket p) {
out.writeByte(P_IDENTITY_RESPONSE); writeUuid(out, p.serverId());
} else if (packet instanceof Backend2Proxy.DataSyncReceivedPacket) {
out.writeByte(P_DATA_SYNC_RECEIVED);
} else if (packet instanceof Backend2Proxy.KeepAlivePacket) {
out.writeByte(P_KEEPALIVE);
} else if (packet instanceof Backend2Proxy.AnnouncePlayerPermissionChanges p) {
out.writeByte(P_PLAYER_PERMISSION_CHANGE); writeUuid(out, p.playerId());
} else if (packet instanceof Backend2Proxy.AnnounceGroupPermissionChanges p) {
out.writeByte(P_GROUP_PERMISSION_CHANGE);
out.writeInt(p.playerIds().size());
for (UUID uuid : p.playerIds()) writeUuid(out, uuid);
} else {
throw new IOException("Unknown packet type");
}
}
return output;
if (buffer.size() > MAX_PLAINTEXT_BYTES) throw new IOException("Packet too large");
return buffer.toByteArray();
}
public static boolean isValidPacket(Object object) {
return object instanceof PATPacket;
}
private static PATPacket decodePacket(byte[] bytes, boolean initial) throws IOException {
if (bytes.length == 0 || bytes.length > MAX_PLAINTEXT_BYTES) throw new IOException("Invalid packet length");
try (DataInputStream in = new DataInputStream(new ByteArrayInputStream(bytes))) {
final int type = in.readUnsignedByte();
final PATPacket packet = switch (type) {
case P_UPDATE -> new Proxy2Backend.UpdatePacket(readNullableUuid(in));
case P_IDENTITY -> new Proxy2Backend.IdentityPacket(readUuid(in), readString(in));
case P_NOTIFICATION -> new Proxy2Backend.NotificationPacket(readUuid(in), readString(in));
case P_EXEC_CONSOLE -> new Proxy2Backend.ExecuteConsoleCommandPacket(readString(in));
case P_EXEC_PLAYER -> new Proxy2Backend.ExecutePlayerCommandPacket(readUuid(in), readString(in));
case P_CONSOLE_MESSAGE -> new Proxy2Backend.ConsoleMessagePacket(readString(in));
case P_KEEPALIVE_RESPONSE -> new Proxy2Backend.KeepAliveResponsePacket();
case P_DATA_SYNC -> {
final Proxy2Backend.DataSyncPacket.Messages messages =
new Proxy2Backend.DataSyncPacket.Messages(readString(in));
final Proxy2Backend.DataSyncPacket.AutoLowerCase autoLowerCase =
new Proxy2Backend.DataSyncPacket.AutoLowerCase(in.readBoolean());
final boolean unknownEnabled = in.readBoolean();
final int count = readCount(in, Integer.BYTES);
final List<String> lines = new ArrayList<>(count);
for (int i = 0; i < count; i++) lines.add(readString(in));
final Proxy2Backend.DataSyncPacket.UnknownCommand unknown =
new Proxy2Backend.DataSyncPacket.UnknownCommand(
unknownEnabled, new MultipleMessagesHelper(lines));
yield new Proxy2Backend.DataSyncPacket(messages, autoLowerCase, unknown);
}
case P_IDENTITY_REQUEST -> new Backend2Proxy.IdentityRequestPacket(readUuid(in));
case P_IDENTITY_RESPONSE -> new Backend2Proxy.IdentityResponsePacket(readUuid(in));
case P_DATA_SYNC_RECEIVED -> new Backend2Proxy.DataSyncReceivedPacket();
case P_KEEPALIVE -> new Backend2Proxy.KeepAlivePacket();
case P_PLAYER_PERMISSION_CHANGE -> new Backend2Proxy.AnnouncePlayerPermissionChanges(readUuid(in));
case P_GROUP_PERMISSION_CHANGE -> {
final int count = readCount(in, 2 * Long.BYTES);
final Set<UUID> ids = new HashSet<>(Math.max(16, count));
for (int i = 0; i < count; i++) ids.add(readUuid(in));
yield new Backend2Proxy.AnnounceGroupPermissionChanges(ids);
}
default -> throw new IOException("Unknown packet type: " + type);
};
public static boolean isP2BPacket(Object object) {
return object instanceof P2BPacket;
}
public static boolean isB2PPacket(Object object) {
return object instanceof B2PPacket;
}
public static boolean isInitialPacket(Object object) {
return object instanceof InitialPacket;
}
public static boolean isInitialPacket(byte[] bytes) {
return bytes.length > 0 && isInitialPacket(bytes[0]);
}
public static boolean isInitialPacket(byte b) {
return b == INIT_PACKET;
}
public interface PATPacket extends Serializable {
String token = Storage.TOKEN;
default boolean tokenMatches(String token) {
return PATPacket.token.equals(token);
if (in.available() != 0) throw new IOException("Trailing packet data");
if (initial != isInitialPacket(packet)) throw new IOException("Packet/header type mismatch");
return packet;
}
}
// Reject oversized plaintext as it is written, rather than after allocating it.
private static final class BoundedOutputStream extends FilterOutputStream {
private final int limit;
private int written;
private interface InitialPacket extends PATPacket {}
private interface Synchronizable extends Serializable {}
private BoundedOutputStream(OutputStream out, int limit) {
super(out);
this.limit = limit;
}
private interface P2BPacket extends PATPacket {}
private interface B2PPacket extends PATPacket {}
@Override
public void write(int value) throws IOException {
if (written >= limit) throw new IOException("Packet too large");
out.write(value);
written++;
}
@Override
public void write(byte[] bytes, int offset, int length) throws IOException {
if (length > limit - written) throw new IOException("Packet too large");
out.write(bytes, offset, length);
written += length;
}
}
private static void writeString(DataOutputStream out, String value) throws IOException {
if (value == null) throw new IOException("Null string");
final byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
out.writeInt(bytes.length);
out.write(bytes);
}
private static String readString(DataInputStream in) throws IOException {
final int length = in.readInt();
if (length < 0 || length > in.available()) {
throw new IOException("Invalid string length");
}
return new String(in.readNBytes(length), StandardCharsets.UTF_8);
}
private static int readCount(DataInputStream in, int minimumEntryBytes) throws IOException {
final int count = in.readInt();
if (count < 0 || count > in.available() / minimumEntryBytes) {
throw new IOException("Invalid collection size");
}
return count;
}
private static void writeUuid(DataOutputStream out, UUID uuid) throws IOException {
if (uuid == null) throw new IOException("Null UUID");
out.writeLong(uuid.getMostSignificantBits());
out.writeLong(uuid.getLeastSignificantBits());
}
private static UUID readUuid(DataInputStream in) throws IOException {
return new UUID(in.readLong(), in.readLong());
}
private static void writeNullableUuid(DataOutputStream out, UUID uuid) throws IOException {
out.writeBoolean(uuid != null);
if (uuid != null) writeUuid(out, uuid);
}
private static UUID readNullableUuid(DataInputStream in) throws IOException {
return in.readBoolean() ? readUuid(in) : null;
}
public static boolean isValidPacket(Object object) { return object instanceof PATPacket; }
public static boolean isP2BPacket(Object object) { return object instanceof P2BPacket; }
public static boolean isB2PPacket(Object object) { return object instanceof B2PPacket; }
public static boolean isInitialPacket(Object object) { return object instanceof InitialPacket; }
// Routing hint only; the flag is authenticated by readPacket().
public static boolean isInitialPacket(byte[] bytes) { return bytes != null && bytes.length > 1 && bytes[0] == PROTOCOL_VERSION && bytes[1] == INIT_PACKET; }
public interface PATPacket { }
private interface InitialPacket extends PATPacket { }
private interface Synchronizable { }
private interface P2BPacket extends PATPacket { }
private interface B2PPacket extends PATPacket { }
public static class Proxy2Backend {
private Proxy2Backend() { }
public record UpdatePacket(UUID playerId) implements P2BPacket {
public boolean forEveryone() { return playerId == null; }
}
public record UpdatePacket(UUID playerId) implements P2BPacket { public boolean forEveryone() { return playerId == null; } }
public record IdentityPacket(UUID targetServerId, String serverName) implements InitialPacket, P2BPacket { }
public record NotificationPacket(UUID playerId, String command) implements P2BPacket { }
public record ExecuteConsoleCommandPacket(String command) implements P2BPacket { }
public record ExecutePlayerCommandPacket(UUID playerId, String command) implements P2BPacket { }
public record ConsoleMessagePacket(String message) implements P2BPacket { }
public record KeepAliveResponsePacket() implements P2BPacket { }
public record DataSyncPacket(
Messages messages,
AutoLowerCase autoLowerCase,
UnknownCommand unknownCommand
) implements P2BPacket {
public record Messages(String prefix)
implements Synchronizable {}
public record AutoLowerCase(boolean enabled)
implements Synchronizable {}
public record UnknownCommand(boolean enabled, MultipleMessagesHelper message)
implements Synchronizable {}
public record DataSyncPacket(Messages messages, AutoLowerCase autoLowerCase, UnknownCommand unknownCommand) implements P2BPacket {
public record Messages(String prefix) implements Synchronizable { }
public record AutoLowerCase(boolean enabled) implements Synchronizable { }
public record UnknownCommand(boolean enabled, MultipleMessagesHelper message) implements Synchronizable { }
}
}
public static class Backend2Proxy {
private Backend2Proxy() { }
public record IdentityRequestPacket(UUID serverId) implements InitialPacket, B2PPacket { }
public record IdentityResponsePacket(UUID serverId) implements InitialPacket, B2PPacket { }
public record DataSyncReceivedPacket() implements B2PPacket { }
public record KeepAlivePacket() implements B2PPacket { }
public record AnnouncePlayerPermissionChanges(UUID playerId) implements B2PPacket { }
public record AnnounceGroupPermissionChanges(Set<UUID> playerIds) implements B2PPacket { }
}
}
@@ -66,10 +66,14 @@ public class Reflection {
return software == Software.FOLIA;
}
public static boolean isCraftbukkit() {
public static boolean isBukkit() {
return software == Software.BUKKIT;
}
public static boolean isSpigot() {
return software == Software.SPIGOT;
}
public static boolean isArclight() {
return software == Software.ARCLIGHT;
}
@@ -77,6 +81,11 @@ public class Reflection {
public static boolean isProxyServer() {
return software.isProxySoftware();
}
public static boolean isBungeeServer() {
return software == Software.BUNGEECORD;
}
public static boolean isVelocityServer() {
return software == Software.VELOCITY;
}
@@ -1,9 +1,7 @@
package de.rayzs.pat.utils;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.*;
public class StringUtils {
@@ -26,7 +24,7 @@ public class StringUtils {
String line = lines.get(i);
if (line.startsWith(centerVariable)) {
line = MessageTranslator.colorless(line.substring(centerVariable.length()));
line = MessageHelper.strip(line.substring(centerVariable.length()));
int size = line.length();
sizes.put(i, size);
@@ -51,31 +49,6 @@ public class StringUtils {
}
}
public static String hashString(final String string, final String algorithm) {
try {
final MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
final byte[] hash = messageDigest.digest(string.getBytes(StandardCharsets.UTF_8));
final StringBuilder hexString = new StringBuilder(2 * hash.length);
for (final byte b : hash) {
final String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (Exception exception) {
exception.printStackTrace();
}
return null;
}
public static String replaceFirst(String input, String trigger, String replacement) {
if (!input.contains(trigger)) {
@@ -151,28 +124,33 @@ public class StringUtils {
return input;
}
public static String replace(List<String> input, String... replacements) {
return replace(String.join("\n", input), replacements);
}
public static String replace(String input, String... replacements) {
final HashMap<String, String> REPLACEMENTS = new HashMap<>();
if (replacements.length == 0) {
return input;
}
if (replacements.length > 0) {
String firstReplacementInput = null, secondReplacementInput = null;
for (String replacement : replacements) {
if (firstReplacementInput == null) firstReplacementInput = replacement;
else secondReplacementInput = replacement;
final HashMap<String, String> cache = new HashMap<>();
String firstReplacementInput = null, secondReplacementInput = null;
if (firstReplacementInput != null && secondReplacementInput != null) {
REPLACEMENTS.put(firstReplacementInput, secondReplacementInput);
firstReplacementInput = null;
secondReplacementInput = null;
}
for (String replacement : replacements) {
if (firstReplacementInput == null) firstReplacementInput = replacement;
else secondReplacementInput = replacement;
if (firstReplacementInput != null && secondReplacementInput != null) {
cache.put(firstReplacementInput, secondReplacementInput);
firstReplacementInput = null;
secondReplacementInput = null;
}
}
String text = input;
if (replacements.length > 0)
for (Map.Entry<String, String> entry : REPLACEMENTS.entrySet())
text = text.replace(entry.getKey(), entry.getValue());
for (Map.Entry<String, String> entry : cache.entrySet())
text = text.replace(entry.getKey(), entry.getValue());
return text;
}
@@ -8,6 +8,11 @@ public class MultipleMessagesHelper implements Serializable {
private final List<String> lines;
/** Construct a message set from authenticated wire data. */
public MultipleMessagesHelper(List<String> lines) {
this.lines = new ArrayList<>(lines);
}
public MultipleMessagesHelper(ConfigStorage config, String path, List<String> input) {
final ConfigSectionHelper<ArrayList<String>> sectionHelper = new ConfigSectionHelper<>(config, path, input);
final Object resultObj = sectionHelper.getOrSet();
@@ -209,8 +209,9 @@ public class LuckPermsHook {
private static boolean isRelevantPermission(Node node) {
final String key = node.getKey();
return key.startsWith("group.")
return key.equals("*")
|| key.startsWith("group.")
|| key.startsWith("proantitab.")
|| key.equals("*");
|| key.startsWith("-proantitab.");
}
}
@@ -0,0 +1,104 @@
package de.rayzs.pat.utils.message;
import de.rayzs.pat.plugin.logger.Logger;
import de.rayzs.pat.utils.configuration.helper.MultipleMessagesHelper;
import de.rayzs.pat.utils.message.replacer.PlaceholderReplacer;
import de.rayzs.pat.utils.message.translators.*;
import de.rayzs.pat.utils.*;
import de.rayzs.pat.utils.sender.CommandSender;
import java.sql.Ref;
import java.util.List;
public class MessageHelper {
private static boolean support;
private static Translator translator = null;
public static void initialize() {
if (Reflection.isBukkit() || Reflection.isSpigot()) {
Logger.warning("MiniMessage support disabled! Your server software is too old. Please switch to a more modern paper-based fork such as Paper, Purpur, Leaf..., etc to enable MiniMessage support.");
return;
}
if (Reflection.isBungeeServer() && !Reflection.doesClassExist("net.kyori.adventure.util.Buildable$Builder")) {
Logger.warning("MiniMessage support disabled! Since MiniMessage isn't supported for Bungeecord anymore and this version of Bungeecord is too advanced, MiniMessage support is disabled!");
return;
}
support = true;
translator = Reflection.isVelocityServer() ? new VelocityMessageTranslator()
: Reflection.isProxyServer() ? new BungeeMessageTranslator()
: Reflection.isPaper() ? new PaperMessageTranslator()
: new BukkitMessageTranslator();
}
public static void sendMessage(CommandSender sender, MultipleMessagesHelper texts, String... replacements) {
sendMessage(sender, texts.getLines(), replacements);
}
public static void sendMessage(CommandSender sender, List<String> texts, String... replacements) {
sendMessage(sender, StringUtils.getStringList(texts, "\n"), replacements);
}
public static void sendMessage(CommandSender sender, String text, String... replacements) {
if (text.isEmpty()) return;
if (!Reflection.isProxyServer()) {
text = PlaceholderReplacer.applyPlaceholders(sender, text, replacements);
if (support) {
translator.send(sender, text);
return;
}
sender.sendMessage(text);
return;
}
if (support) {
PlaceholderReplacer.applyProxyPlaceholders(sender, text, result -> translator.send(sender, result), replacements);
return;
}
PlaceholderReplacer.applyProxyPlaceholders(sender, text, sender::sendMessage, replacements);
}
public static void sendActionbar(CommandSender sender, String message) {
if (!support) {
sender.sendActionbar(message);
return;
}
translator.sendActionbar(sender, message);
}
public static void sendTitle(CommandSender sender, String title, String subtitle, int fadeIn, int stay, int fadeOut) {
if (!support) {
sender.sendTitle(title, subtitle, fadeIn, stay, fadeOut);
return;
}
translator.sendTitle(sender, title, subtitle, fadeIn, stay, fadeOut);
}
public static void playSound(CommandSender sender, String soundKey, float volume, float pitch) throws Exception {
translator.playSound(sender, soundKey, volume, pitch);
}
public static String strip(String text) {
return translator.strip(text);
}
public static void closeAudiences() {
if (translator == null) return;
translator.close();
}
public static boolean isSupported() {
return support;
}
}
@@ -1,229 +0,0 @@
package de.rayzs.pat.utils.message;
import de.rayzs.pat.plugin.logger.Logger;
import de.rayzs.pat.utils.configuration.helper.MultipleMessagesHelper;
import de.rayzs.pat.utils.message.replacer.PlaceholderReplacer;
import de.rayzs.pat.utils.message.translators.*;
import de.rayzs.pat.api.storage.Storage;
import de.rayzs.pat.utils.*;
import de.rayzs.pat.utils.sender.CommandSender;
import java.util.*;
public class MessageTranslator {
private static final HashMap<Character, String> colors = new HashMap<>(), endingColors = new HashMap<>();
private static boolean support;
private static Translator translator = null;
public static void initialize() {
colors.put('1', "<dark_blue>");
colors.put('2', "<dark_green>");
colors.put('3', "<dark_aqua>");
colors.put('4', "<dark_red>");
colors.put('5', "<dark_purple>");
colors.put('6', "<gold>");
colors.put('7', "<gray>");
colors.put('8', "<dark_gray>");
colors.put('9', "<blue>");
colors.put('a', "<green>");
colors.put('b', "<aqua>");
colors.put('c', "<red>");
colors.put('d', "<light_purple>");
colors.put('e', "<yellow>");
colors.put('f', "<white>");
colors.put('m', "<st>");
colors.put('k', "<obf>");
colors.put('r', "<reset>");
colors.put('o', "<i>");
colors.put('l', "<b>");
for (Map.Entry<Character, String> entry : colors.entrySet())
endingColors.put(entry.getKey(), "</" + entry.getValue().substring(1));
support = !Reflection.isArclight()
&& !Reflection.isCraftbukkit()
&& (Reflection.isAtLeast(1, 18) || Reflection.isProxyServer());
if (Reflection.isAtLeast(26, 2) && !Reflection.getSoftware().isPaperBased()) {
support = false;
Logger.warning("Due to an incompatibility issue, MiniMessage support is not given for non-Paper based server softwares as of now!");
}
if (Reflection.getSoftware() == Reflection.Software.BUNGEECORD && !Reflection.doesClassExist("net.kyori.adventure.util.Buildable$Builder")) {
support = false;
Logger.warning("Since MiniMessage isn't supported for Bungeecord anymore and this version of Bungeecord is too advanced, MiniMessage support is disabled!");
}
if (support) {
translator = Reflection.isVelocityServer() ? new VelocityMessageTranslator()
: Reflection.isProxyServer() ? new BungeeMessageTranslator()
: new BukkitMessageTranslator();
}
}
public static String translateLegacy(String text) {
text = replaceMessage(text);
if(!text.contains("§")) return text;
for (Map.Entry<Character, String> entry : colors.entrySet())
text = text.replace("§" + entry.getKey(), entry.getValue());
return text;
}
public static String colorless(String text) {
text = replaceMessage(text);
if(!text.contains("§")) return text;
for (Map.Entry<Character, String> entry : colors.entrySet())
text = text.replace("§" + entry.getKey(), "");
return text;
}
public static void send(Object target, MultipleMessagesHelper texts) {
String stringList = StringUtils.getStringList(texts.getLines(), "\n");
send(target, stringList);
}
public static void send(Object target, MultipleMessagesHelper texts, String... replacements) {
String stringList = StringUtils.getStringList(texts.getLines(), "\n");
send(target, stringList, replacements);
}
public static void send(Object target, List<String> texts) {
String stringList = StringUtils.getStringList(texts, "\n");
send(target, stringList);
}
public static void send(Object target, List<String> texts, String... replacements) {
texts = replaceMessageList(texts, replacements);
send(target, texts);
}
public static void send(Object target, String text, String... replacements) {
if (text.isEmpty()) return;
text = replaceMessageString(target, text, replacements);
if (text.contains("%no-message%")) return;
if(translator == null) {
CommandSender sender = target instanceof CommandSender
? (CommandSender) target
: CommandSender.from(target);
if (!PlaceholderReplacer.process(sender, text, sender::sendMessage))
sender.sendMessage(text);
return;
}
final Object tmpTarget = target instanceof CommandSender sender
? sender.getSenderObject() : target;
if (PlaceholderReplacer.process(target, text, result -> translator.send(tmpTarget, result))) {
return;
}
translator.send(tmpTarget, text);
}
public static String replaceMessage(String text) {
return replaceMessage(null, text);
}
public static String replaceMessage(Object targetObj, String text) {
CommandSender sender = targetObj == null ? null
: targetObj instanceof CommandSender
? (CommandSender) targetObj
: CommandSender.from(targetObj);
String executorName = (sender == null || sender.isConsole())
? ""
: sender.getName();
String token = (Storage.ConfigSections.Settings.HANDLE_THROUGH_PROXY.ENABLED || Reflection.isProxyServer())
? Storage.CENSORED_TOKEN
: "/";
String serverName = Storage.SERVER_NAME == null ? "/" : Storage.SERVER_NAME;
text = StringUtils.replace(text,
"&", "§",
"\\n", "\n",
"%executor%", executorName,
"%prefix%", Storage.ConfigSections.Messages.PREFIX.PREFIX,
"%pat_general_prefix%", Storage.ConfigSections.Messages.PREFIX.PREFIX,
"%token%", token,
"%sync_server_name%", serverName,
"%server%", serverName,
"%current_version%", Storage.CURRENT_VERSION,
"%newest_version%", Storage.NEWER_VERSION
);
return PlaceholderReplacer.replace(targetObj, text);
}
public static String replaceMessageString(Object targetObj, String rawText, String... replacements) {
return replaceMessage(targetObj, StringUtils.replace(rawText, replacements));
}
public static List<String> replaceMessageList(MultipleMessagesHelper rawText, String... replacements) {
return replaceMessageList(rawText.getLines(), replacements);
}
public static List<String> replaceMessageList(List<String> rawText, String... replacements) {
final HashMap<String, String> REPLACEMENTS = new HashMap<>();
List<String> result = new ArrayList<>();
if(replacements != null) {
String firstReplacementInput = null, secondReplacementInput = null;
for (String replacement : replacements) {
if (firstReplacementInput == null) firstReplacementInput = replacement;
else secondReplacementInput = replacement;
if (firstReplacementInput != null && secondReplacementInput != null) {
REPLACEMENTS.put(firstReplacementInput, secondReplacementInput);
firstReplacementInput = null;
secondReplacementInput = null;
}
}
}
rawText.forEach(text -> {
if(replacements != null)
for (Map.Entry<String, String> entry : REPLACEMENTS.entrySet())
text = text.replace(entry.getKey(), entry.getValue());
String resultText = replaceMessage(text);
result.add(resultText);
});
return result;
}
public static void sendActionbar(Object targetObj, String message) {
translator.sendActionbar(targetObj, message);
}
public static void sendTitle(Object targetObj, String title, String subtitle, int fadeIn, int stay, int fadeOut) {
translator.sendTitle(targetObj, title, subtitle, fadeIn, stay, fadeOut);
}
public static void playSound(Object targetObj, String soundKey, float volume, float pitch) throws Exception {
translator.playSound(targetObj, soundKey, volume, pitch);
}
public static void closeAudiences() {
if(translator == null) return;
translator.close();
}
public static boolean isSupported() {
return support;
}
}
@@ -1,6 +1,7 @@
package de.rayzs.pat.utils.message;
import de.rayzs.pat.utils.StringUtils;
import de.rayzs.pat.utils.sender.CommandSender;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
@@ -9,21 +10,21 @@ public interface Translator {
MiniMessage miniMessage = MiniMessage.miniMessage();
String translate(String text);
String strip(String text);
void send(Object target, String text);
void send(CommandSender sender, String text);
void sendActionbar(Object target, String text);
void sendActionbar(CommandSender sender, String text);
void sendTitle(Object target, String title, String subtitle, int fadeIn, int stay, int fadeOut);
void sendTitle(CommandSender sender, String title, String subtitle, int fadeIn, int stay, int fadeOut);
void playSound(Object target, String soundKey, float volume, float pitch) throws Exception;
void playSound(CommandSender sender, String soundKey, float volume, float pitch) throws Exception;
default Component toComponent(String text) {
text = StringUtils.replace(text, "§", "&");
Component legacy = LegacyComponentSerializer.legacyAmpersand().deserialize(text);
text = StringUtils.replace(miniMessage.serialize(legacy), "\\", "");
final Component legacy = LegacyComponentSerializer.legacyAmpersand().deserialize(text);
text = miniMessage.serialize(legacy).replace("\\", "");
return miniMessage.deserialize(text);
}
@@ -1,7 +1,12 @@
package de.rayzs.pat.utils.message.replacer;
import de.rayzs.pat.utils.Reflection;
import de.rayzs.pat.utils.StringUtils;
import de.rayzs.pat.utils.message.replacer.impl.*;
import de.rayzs.pat.api.storage.Storage;
import de.rayzs.pat.utils.sender.CommandSender;
import java.util.Objects;
import java.util.function.Consumer;
public class PlaceholderReplacer {
@@ -9,8 +14,10 @@ public class PlaceholderReplacer {
private static BukkitPlaceholderReplacer BUKKIT_PLACEHOLDER_REPLACER = null;
private static ProxyPlaceholderReplacer PROXY_PLACEHOLDER_REPLACER = null;
public static String replace(Object playerObj, String text) {
if (!Storage.USE_PLACEHOLDERAPI)
public static String applyPlaceholders(CommandSender sender, String text, String... replacements) {
text = applyGeneralReplacements(sender, text, replacements);
if (Reflection.isProxyServer() || !Storage.USE_PLACEHOLDERAPI)
return text;
if (!text.contains("%"))
@@ -19,18 +26,51 @@ public class PlaceholderReplacer {
if (BUKKIT_PLACEHOLDER_REPLACER == null)
BUKKIT_PLACEHOLDER_REPLACER = new BukkitPlaceholderReplacer();
return BUKKIT_PLACEHOLDER_REPLACER.process(playerObj, text);
return BUKKIT_PLACEHOLDER_REPLACER.process(sender, text);
}
public static boolean process(Object playerObj, String text, Consumer<String> consumer) {
if (!Storage.USE_PAPIPROXYBRIDGE)
return false;
public static void applyProxyPlaceholders(CommandSender sender, String text, Consumer<String> consumer, String... replacements) {
text = applyPlaceholders(sender, text, replacements);
if (!text.contains("%")) return false;
if (!Storage.USE_PAPIPROXYBRIDGE) {
consumer.accept(text);
return;
}
if (PROXY_PLACEHOLDER_REPLACER == null)
if (!text.contains("%")) {
consumer.accept(text);
return;
}
if (PROXY_PLACEHOLDER_REPLACER == null) {
PROXY_PLACEHOLDER_REPLACER = new ProxyPlaceholderReplacer();
}
return PROXY_PLACEHOLDER_REPLACER.process(playerObj, text, consumer);
if (!PROXY_PLACEHOLDER_REPLACER.process(sender, text, consumer)) {
consumer.accept(text);
}
}
private static String applyGeneralReplacements(CommandSender sender, String text, String... replacements) {
final String executorName = sender == null ? "" : sender.getName();
final String token = (Storage.ConfigSections.Settings.HANDLE_THROUGH_PROXY.ENABLED || Reflection.isProxyServer())
? Storage.CENSORED_TOKEN
: "/";
final String serverName = Objects.requireNonNullElse(Storage.SERVER_NAME, "/");
return StringUtils.replace(StringUtils.replace(text, replacements),
"&", "§",
"\\n", "\n",
"%executor%", executorName,
"%prefix%", Storage.ConfigSections.Messages.PREFIX.PREFIX,
"%pat_general_prefix%", Storage.ConfigSections.Messages.PREFIX.PREFIX,
"%token%", token,
"%sync_server_name%", serverName,
"%server%", serverName,
"%current_version%", Storage.CURRENT_VERSION,
"%newest_version%", Storage.NEWER_VERSION
);
}
}
@@ -6,17 +6,12 @@ import org.bukkit.entity.Player;
public class BukkitPlaceholderReplacer {
public String process(Object playerObj, String text) {
Player player = null;
public String process(CommandSender sender, String text) {
if(playerObj != null)
if(playerObj instanceof CommandSender) {
CommandSender sender = (CommandSender) playerObj;
player = sender.isPlayer() ? (Player) sender.getSenderObject() : null;
} else if(playerObj instanceof Player)
player = (Player) playerObj;
if (sender.isPlayer() && sender.getSenderObject() instanceof Player player) {
return PlaceholderAPI.setPlaceholders(player, text);
}
return PlaceholderAPI.setPlaceholders(player, text);
return PlaceholderAPI.setPlaceholders(null, text);
}
}
@@ -15,33 +15,10 @@ public class ProxyPlaceholderReplacer {
placeholderAPI.setRequestTimeout(1500);
}
public boolean process(Object playerObj, String text, Consumer<String> consumer) {
UUID uuid = null;
if(playerObj != null)
if (playerObj instanceof CommandSender) {
uuid = ((CommandSender) playerObj).getUniqueId();
} else if (Reflection.isVelocityServer()) {
if (playerObj instanceof com.velocitypowered.api.proxy.Player)
uuid = ((com.velocitypowered.api.proxy.Player) playerObj).getUniqueId();
} else if (Reflection.isProxyServer()) {
if (playerObj instanceof net.md_5.bungee.api.connection.ProxiedPlayer)
uuid = ((net.md_5.bungee.api.connection.ProxiedPlayer) playerObj).getUniqueId();
} else {
if (playerObj instanceof org.bukkit.entity.Player)
uuid = ((org.bukkit.entity.Player) playerObj).getUniqueId();
}
if(uuid == null) return false;
placeholderAPI.formatPlaceholders(text, uuid).thenAccept(consumer);
public boolean process(CommandSender sender, String text, Consumer<String> consumer) {
if (sender == null || !sender.isPlayer()) return false;
placeholderAPI.formatPlaceholders(text, sender.getUniqueId()).thenAccept(consumer);
return true;
}
}
@@ -1,10 +1,10 @@
package de.rayzs.pat.utils.message.translators;
import de.rayzs.pat.utils.Reflection;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.audience.Audience;
import de.rayzs.pat.plugin.BukkitLoader;
import net.kyori.adventure.title.Title;
import org.bukkit.ChatColor;
import org.bukkit.Sound;
import org.bukkit.command.CommandSender;
import de.rayzs.pat.utils.message.*;
@@ -21,76 +21,67 @@ public class BukkitMessageTranslator implements Translator {
}
@Override
public String translate(String text) {
return miniMessage.serialize(miniMessage.deserialize(MessageTranslator.translateLegacy(text)));
public String strip(String text) {
return ChatColor.stripColor(miniMessage.stripTags(text));
}
@Override
public void send(Object target, String text) {
if (Reflection.getSoftware().isPaperBased()) {
if (target instanceof Player player) {
player.sendMessage(toComponent(text));
} else if (target instanceof CommandSender sender) {
sender.sendMessage(toComponent(text));
}
public void send(de.rayzs.pat.utils.sender.CommandSender sender, String text) {
final Audience audience = sender.getSenderObject() instanceof Player player
? audiences.player(player)
: sender.getSenderObject() instanceof CommandSender commandSender
? audiences.sender(commandSender)
: null;
if (audience == null) {
return;
}
Audience audience = target instanceof Player
? audiences.player((Player) target)
: audiences.sender((CommandSender) target);
audience.sendMessage(toComponent(text));
}
@Override
public void sendActionbar(Object target, String text) {
public void sendActionbar(de.rayzs.pat.utils.sender.CommandSender sender, String text) {
final Audience audience = sender.getSenderObject() instanceof Player player
? audiences.player(player)
: sender.getSenderObject() instanceof CommandSender commandSender
? audiences.sender(commandSender)
: null;
if (Reflection.getSoftware().isPaperBased()) {
if (target instanceof Player player) {
player.sendActionBar(toComponent(text));
} else if (target instanceof CommandSender sender) {
sender.sendActionBar(toComponent(text));
}
if (audience == null) {
return;
}
Audience audience = target instanceof Player
? audiences.player((Player) target)
: audiences.sender((CommandSender) target);
audience.sendActionBar(toComponent(text));
}
@Override
public void sendTitle(Object target, String titleStr, String subtitleStr, int fadeIn, int stay, int fadeOut) {
Title.Times times = Title.Times.times(Duration.ofMillis(fadeIn), Duration.ofMillis(stay), Duration.ofMillis(fadeOut));
Title title = Title.title(toComponent(titleStr), toComponent(subtitleStr), times);
public void sendTitle(de.rayzs.pat.utils.sender.CommandSender sender, String titleStr, String subtitleStr, int fadeIn, int stay, int fadeOut) {
if (Reflection.getSoftware().isPaperBased()) {
if (target instanceof Player player) {
player.showTitle(title);
} else if (target instanceof CommandSender sender) {
sender.showTitle(title);
}
final Audience audience = sender.getSenderObject() instanceof Player player
? audiences.player(player)
: sender.getSenderObject() instanceof CommandSender commandSender
? audiences.sender(commandSender)
: null;
if (audience == null) {
return;
}
Audience audience = target instanceof Player
? audiences.player((Player) target)
: audiences.sender((CommandSender) target);
final Title.Times times = Title.Times.times(Duration.ofMillis(fadeIn), Duration.ofMillis(stay), Duration.ofMillis(fadeOut));
final Title title = Title.title(toComponent(titleStr), toComponent(subtitleStr), times);
audience.showTitle(title);
}
@Override
public void playSound(Object target, String soundKey, float volume, float pitch) throws Exception {
if (target instanceof Player player) {
public void playSound(de.rayzs.pat.utils.sender.CommandSender sender, String soundKey, float volume, float pitch) throws Exception {
if (sender.getSenderObject() instanceof Player player) {
Sound sound = Sound.valueOf(soundKey);
player.playSound(player.getLocation(), sound, volume, pitch);
}
@@ -1,17 +1,15 @@
package de.rayzs.pat.utils.message.translators;
import de.rayzs.pat.utils.StringUtils;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.platform.bungeecord.BungeeAudiences;
import net.kyori.adventure.title.Title;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import de.rayzs.pat.utils.message.MessageTranslator;
import net.kyori.adventure.audience.Audience;
import de.rayzs.pat.utils.message.Translator;
import net.md_5.bungee.api.CommandSender;
import de.rayzs.pat.plugin.BungeeLoader;
import org.bukkit.ChatColor;
import java.time.Duration;
@@ -24,39 +22,79 @@ public class BungeeMessageTranslator implements Translator {
}
@Override
public String translate(String text) {
return miniMessage.serialize(miniMessage.deserialize(MessageTranslator.translateLegacy(text)));
public String strip(String text) {
return ChatColor.stripColor(miniMessage.stripTags(text));
}
@Override
public void send(Object target, String text) {
Audience audience = target instanceof ProxiedPlayer ? audiences.player((ProxiedPlayer) target) : audiences.sender((CommandSender) target);
public void send(de.rayzs.pat.utils.sender.CommandSender sender, String text) {
final Audience audience = sender.getSenderObject() instanceof ProxiedPlayer player
? audiences.player(player)
: sender.getSenderObject() instanceof CommandSender commandSender
? audiences.sender(commandSender)
: null;
if (audience == null) {
return;
}
audience.sendMessage(toComponent(text));
}
@Override
public void sendActionbar(Object target, String text) {
Audience audience = target instanceof ProxiedPlayer ? audiences.player((ProxiedPlayer) target) : audiences.sender((CommandSender) target);
public void sendActionbar(de.rayzs.pat.utils.sender.CommandSender sender, String text) {
final Audience audience = sender.getSenderObject() instanceof ProxiedPlayer player
? audiences.player(player)
: sender.getSenderObject() instanceof CommandSender commandSender
? audiences.sender(commandSender)
: null;
if (audience == null) {
return;
}
audience.sendActionBar(toComponent(text));
}
@Override
public void sendTitle(Object target, String titleStr, String subtitleStr, int fadeIn, int stay, int fadeOut) {
Audience audience = target instanceof ProxiedPlayer ? audiences.player((ProxiedPlayer) target) : audiences.sender((CommandSender) target);
public void sendTitle(de.rayzs.pat.utils.sender.CommandSender sender, String titleStr, String subtitleStr, int fadeIn, int stay, int fadeOut) {
final Audience audience = sender.getSenderObject() instanceof ProxiedPlayer player
? audiences.player(player)
: sender.getSenderObject() instanceof CommandSender commandSender
? audiences.sender(commandSender)
: null;
Title.Times times = Title.Times.times(Duration.ofMillis(fadeIn), Duration.ofMillis(stay), Duration.ofMillis(fadeOut));
Title title = Title.title(toComponent(titleStr), toComponent(subtitleStr), times);
if (audience == null) {
return;
}
final Title.Times times = Title.Times.times(Duration.ofMillis(fadeIn), Duration.ofMillis(stay), Duration.ofMillis(fadeOut));
final Title title = Title.title(toComponent(titleStr), toComponent(subtitleStr), times);
audience.showTitle(title);
}
@Override
public void playSound(Object target, String soundKey, float volume, float pitch) throws Exception {
Audience audience = target instanceof ProxiedPlayer ? audiences.player((ProxiedPlayer) target) : audiences.sender((CommandSender) target);
Sound sound = Sound.sound(Key.key(soundKey), Sound.Source.MASTER, 1f, 1f);
public void playSound(de.rayzs.pat.utils.sender.CommandSender sender, String soundKey, float volume, float pitch) throws Exception {
final Audience audience = sender.getSenderObject() instanceof ProxiedPlayer player
? audiences.player(player)
: sender.getSenderObject() instanceof CommandSender commandSender
? audiences.sender(commandSender)
: null;
if (audience == null) {
return;
}
final Sound sound = Sound.sound(Key.key(soundKey), Sound.Source.MASTER, 1f, 1f);
audience.playSound(sound, Sound.Emitter.self());
}
@@ -0,0 +1,59 @@
package de.rayzs.pat.utils.message.translators;
import de.rayzs.pat.utils.message.Translator;
import net.kyori.adventure.title.Title;
import org.bukkit.ChatColor;
import org.bukkit.Sound;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.time.Duration;
public class PaperMessageTranslator implements Translator {
@Override
public String strip(String text) {
return ChatColor.stripColor(miniMessage.stripTags(text));
}
@Override
public void send(de.rayzs.pat.utils.sender.CommandSender sender, String text) {
if (sender.getSenderObject() instanceof Player player) {
player.sendMessage(toComponent(text));
} else if (sender.getSenderObject() instanceof CommandSender commandSender) {
commandSender.sendMessage(toComponent(text));
}
}
@Override
public void sendActionbar(de.rayzs.pat.utils.sender.CommandSender sender, String text) {
if (sender.getSenderObject() instanceof Player player) {
player.sendActionBar(toComponent(text));
} else if (sender.getSenderObject() instanceof CommandSender commandSender) {
commandSender.sendActionBar(toComponent(text));
}
}
@Override
public void sendTitle(de.rayzs.pat.utils.sender.CommandSender sender, String titleStr, String subtitleStr, int fadeIn, int stay, int fadeOut) {
final Title.Times times = Title.Times.times(Duration.ofMillis(fadeIn), Duration.ofMillis(stay), Duration.ofMillis(fadeOut));
final Title title = Title.title(toComponent(titleStr), toComponent(subtitleStr), times);
if (sender.getSenderObject() instanceof Player player) {
player.showTitle(title);
} else if (sender.getSenderObject() instanceof CommandSender commandSender) {
commandSender.showTitle(title);
}
}
@Override
public void playSound(de.rayzs.pat.utils.sender.CommandSender sender, String soundKey, float volume, float pitch) throws Exception {
if (sender.getSenderObject() instanceof Player player) {
final Sound sound = Sound.valueOf(soundKey);
player.playSound(player.getLocation(), sound, volume, pitch);
}
}
@Override
public void close() {}
}
@@ -12,39 +12,38 @@ import java.time.Duration;
public class VelocityMessageTranslator implements Translator {
@Override
public String translate(String text) {
return MiniMessage.miniMessage().serialize(miniMessage.deserialize(MessageTranslator.translateLegacy(text)));
public String strip(String text) {
return miniMessage.stripTags(text);
}
@Override
public void send(Object target, String text) {
if (target instanceof CommandSource source) {
public void send(de.rayzs.pat.utils.sender.CommandSender sender, String text) {
if (sender.getSenderObject() instanceof CommandSource source) {
source.sendMessage(toComponent(text));
}
}
@Override
public void sendActionbar(Object target, String text) {
if (target instanceof CommandSource source) {
public void sendActionbar(de.rayzs.pat.utils.sender.CommandSender sender, String text) {
if (sender.getSenderObject() instanceof CommandSource source) {
source.sendActionBar(toComponent(text));
}
}
@Override
public void sendTitle(Object target, String titleStr, String subtitleStr, int fadeIn, int stay, int fadeOut) {
Title.Times times = Title.Times.times(Duration.ofMillis(fadeIn), Duration.ofMillis(stay), Duration.ofMillis(fadeOut));
Title title = Title.title(toComponent(titleStr), toComponent(subtitleStr), times);
public void sendTitle(de.rayzs.pat.utils.sender.CommandSender sender, String titleStr, String subtitleStr, int fadeIn, int stay, int fadeOut) {
final Title.Times times = Title.Times.times(Duration.ofMillis(fadeIn), Duration.ofMillis(stay), Duration.ofMillis(fadeOut));
final Title title = Title.title(toComponent(titleStr), toComponent(subtitleStr), times);
if (target instanceof CommandSource source) {
if (sender.getSenderObject() instanceof CommandSource source) {
source.showTitle(title);
}
}
@Override
public void playSound(Object target, String soundKey, float volume, float pitch) throws Exception {
if (target instanceof CommandSource source) {
Sound sound = Sound.sound(Key.key(soundKey), Sound.Source.MASTER, 1f, 1f);
public void playSound(de.rayzs.pat.utils.sender.CommandSender sender, String soundKey, float volume, float pitch) throws Exception {
if (sender.getSenderObject() instanceof CommandSource source) {
final Sound sound = Sound.sound(Key.key(soundKey), Sound.Source.MASTER, 1f, 1f);
source.playSound(sound, Sound.Emitter.self());
}
}
@@ -92,12 +92,29 @@ public class PermissionUtil {
}
public static void setPermission(UUID uuid, String permission, boolean permitted) {
PermissionMap permissionMap;
PermissionMap permissionMap = MAP.get(uuid);
if(!MAP.containsKey(uuid)) {
if(permissionMap == null) {
permissionMap = new PermissionMap(uuid);
MAP.put(uuid, permissionMap);
} else permissionMap = MAP.get(uuid);
}
final boolean isNegatedPermission = permission.charAt(0) == '-';
// Ignore because set permission is negated.
if (!isNegatedPermission && permissionMap.isPermitted("-" + permission)) {
return;
}
if (isNegatedPermission) {
final String nonNegatedPermission = permission.substring(1);
if (permissionMap.isPermitted(nonNegatedPermission)) {
permissionMap.setState(nonNegatedPermission, false);
return;
}
}
permissionMap.setState(permission, permitted);
}
@@ -169,7 +186,9 @@ public class PermissionUtil {
}
}
return permissionMap.isPermitted("*") || permissionMap.isPermitted("proantitab.*") || permissionMap.isPermitted("proantitab." + permission);
return permissionMap.isPermitted("*")
|| permissionMap.isPermitted("proantitab.*")
|| permissionMap.isPermitted("proantitab." + permission);
}
public static boolean hasBypassPermission(Object targetObj, final boolean isOperator) {
@@ -4,7 +4,8 @@ import de.rayzs.pat.plugin.system.communication.Communicator;
import de.rayzs.pat.api.storage.Storage;
import de.rayzs.pat.plugin.logger.Logger;
import de.rayzs.pat.utils.StringUtils;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.utils.sender.CommandSender;
import java.util.List;
import java.util.UUID;
@@ -25,8 +26,8 @@ public interface Action {
final String displayCommand = StringUtils.replaceTriggers(command, "", "\\", "<", ">", "&");
final List<String> notificationMessage = MessageTranslator.replaceMessageList(
Storage.ConfigSections.Messages.NOTIFICATION.ALERT,
final String notificationMessage = StringUtils.replace(
Storage.ConfigSections.Messages.NOTIFICATION.ALERT.getLines(),
"%player%", playerName,
"%command%", displayCommand,
"%server%", serverName);
@@ -36,10 +37,11 @@ public interface Action {
if (Storage.SEND_CONSOLE_NOTIFICATION)
Logger.info(notificationMessage);
Storage.NOTIFY_PLAYERS.stream().forEach(tmpUUID -> {
Object p = Storage.getLoader().getPlayerObjByUUID(tmpUUID);
if (p != null) {
MessageTranslator.send(p, notificationMessage);
Storage.NOTIFY_PLAYERS.stream().forEach(otherUuid -> {
final CommandSender otherSender = CommandSender.from(otherUuid);
if (otherSender != null) {
MessageHelper.sendMessage(otherSender, notificationMessage);
}
});
}
@@ -1,8 +1,7 @@
package de.rayzs.pat.utils.response.action;
import de.rayzs.pat.utils.response.action.impl.BungeeAction;
import de.rayzs.pat.utils.response.action.impl.ModernBukkitAction;
import de.rayzs.pat.utils.response.action.impl.OldBukkitAction;
import de.rayzs.pat.utils.response.action.impl.BukkitAction;
import de.rayzs.pat.utils.response.action.impl.VelocityAction;
import de.rayzs.pat.utils.Reflection;
import java.util.UUID;
@@ -11,8 +10,7 @@ public class ActionHandler {
private static final Action ACTION = Reflection.isProxyServer() ? Reflection.isVelocityServer()
? new VelocityAction() : new BungeeAction()
: Reflection.isAtMost(1, 16)
? new OldBukkitAction() : new ModernBukkitAction();
: new BukkitAction();
public static void initialize() {}
@@ -2,10 +2,11 @@ package de.rayzs.pat.utils.response.action.impl;
import de.rayzs.pat.plugin.logger.Logger;
import de.rayzs.pat.utils.StringUtils;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.utils.message.replacer.PlaceholderReplacer;
import de.rayzs.pat.utils.response.ResponseHandler;
import de.rayzs.pat.utils.response.action.Action;
import de.rayzs.pat.utils.sender.CommandSender;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
@@ -13,19 +14,19 @@ import org.bukkit.potion.PotionEffectType;
import java.util.UUID;
public class ModernBukkitAction implements Action {
public class BukkitAction implements Action {
@Override
public void executeConsoleCommand(String action, UUID uuid, String command, String message) {
Player player = Bukkit.getPlayer(uuid);
final CommandSender sender = CommandSender.from(uuid);
message = ResponseHandler.replaceArgsVariables(message, command);
if (player != null) {
message = StringUtils.replace(message, "%player%", player.getName());
if (sender != null) {
message = StringUtils.replace(message, "%player%", sender.getName());
}
if (message.contains("%")) {
message = PlaceholderReplacer.replace(player, message);
message = PlaceholderReplacer.applyPlaceholders(sender, message);
}
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), message);
@@ -33,37 +34,37 @@ public class ModernBukkitAction implements Action {
@Override
public void executePlayerCommand(String action, UUID uuid, String command, String message) {
Player player = Bukkit.getPlayer(uuid);
final CommandSender sender = CommandSender.from(uuid);
if (player == null) {
if (sender == null) {
return;
}
message = StringUtils.replace(message, "%player%", player.getName());
message = StringUtils.replace(message, "%player%", sender.getName());
message = ResponseHandler.replaceArgsVariables(message, command);
if (message.contains("%")) {
message = PlaceholderReplacer.replace(player, message);
message = PlaceholderReplacer.applyPlaceholders(sender, message);
}
Bukkit.dispatchCommand(player, message);
sender.execute(message);
}
@Override
public void sendTitle(String action, UUID uuid, String command, String title, String subTitle, int fadeIn, int stay, int fadeOut) {
Player player = Bukkit.getPlayer(uuid);
final CommandSender sender = CommandSender.from(uuid);
if (player == null) {
if (sender == null) {
return;
}
title = ResponseHandler.replaceArgsVariables(title, command);
subTitle = ResponseHandler.replaceArgsVariables(subTitle, command);
title = StringUtils.replace(title, "&", "§", "%player%", player.getName());
subTitle = StringUtils.replace(subTitle, "&", "§", "%player%", player.getName());
title = StringUtils.replace(title, "%player%", sender.getName());
subTitle = StringUtils.replace(subTitle, "%player%", sender.getName());
MessageTranslator.sendTitle(player, title, subTitle, fadeIn, stay, fadeOut);
MessageHelper.sendTitle(sender, title, subTitle, fadeIn, stay, fadeOut);
}
@Override
@@ -92,14 +93,14 @@ public class ModernBukkitAction implements Action {
@Override
public void playSound(String action, UUID uuid, String soundName, float volume, float pitch) {
Player player = Bukkit.getPlayer(uuid);
final CommandSender sender = CommandSender.from(uuid);
if (player == null) {
if (sender == null) {
return;
}
try {
MessageTranslator.playSound(player, soundName, volume, pitch);
MessageHelper.playSound(sender, soundName, volume, pitch);
} catch (Exception exception) {
Logger.warning("! Failed to read action: " + action);
Logger.warning(" > The sound \"" + soundName + "\" could not be found!");
@@ -110,15 +111,15 @@ public class ModernBukkitAction implements Action {
@Override
public void sendActionbar(String action, UUID uuid, String command, String message) {
Player player = Bukkit.getPlayer(uuid);
final CommandSender sender = CommandSender.from(uuid);
if (player == null) {
if (sender == null) {
return;
}
message = StringUtils.replace(message, "%player%", player.getName());
message = StringUtils.replace(message, "%player%", sender.getName());
message = ResponseHandler.replaceArgsVariables(message, command);
MessageTranslator.sendActionbar(player, message);
MessageHelper.sendActionbar(sender, message);
}
}
@@ -2,9 +2,10 @@ package de.rayzs.pat.utils.response.action.impl;
import de.rayzs.pat.plugin.logger.Logger;
import de.rayzs.pat.utils.StringUtils;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.utils.response.ResponseHandler;
import de.rayzs.pat.utils.response.action.Action;
import de.rayzs.pat.utils.sender.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.ProxiedPlayer;
@@ -40,19 +41,19 @@ public class BungeeAction implements Action {
@Override
public void sendTitle(String action, UUID uuid, String command, String title, String subTitle, int fadeIn, int stay, int fadeOut) {
ProxiedPlayer player = ProxyServer.getInstance().getPlayer(uuid);
final CommandSender sender = CommandSender.from(uuid);
if (player == null) {
if (sender == null) {
return;
}
title = ResponseHandler.replaceArgsVariables(title, command);
subTitle = ResponseHandler.replaceArgsVariables(subTitle, command);
title = StringUtils.replace(title, "%player%", player.getName());
subTitle = StringUtils.replace(subTitle, "%player%", player.getName());
title = StringUtils.replace(title, "%player%", sender.getName());
subTitle = StringUtils.replace(subTitle, "%player%", sender.getName());
MessageTranslator.sendTitle(player, title, subTitle, fadeIn, stay, fadeOut);
MessageHelper.sendTitle(sender, title, subTitle, fadeIn, stay, fadeOut);
}
@Override
@@ -61,14 +62,14 @@ public class BungeeAction implements Action {
@Override
public void playSound(String action, UUID uuid, String soundName, float volume, float pitch) {
ProxiedPlayer player = ProxyServer.getInstance().getPlayer(uuid);
final CommandSender sender = CommandSender.from(uuid);
if (player == null) {
if (sender == null) {
return;
}
try {
MessageTranslator.playSound(player, soundName, volume, pitch);
MessageHelper.playSound(sender, soundName, volume, pitch);
} catch (Exception exception) {
Logger.warning("! Failed to read action: " + action);
Logger.warning(" > The sound \"" + soundName + "\" could not be found!");
@@ -79,14 +80,14 @@ public class BungeeAction implements Action {
@Override
public void sendActionbar(String action, UUID uuid, String command, String message) {
ProxiedPlayer player = ProxyServer.getInstance().getPlayer(uuid);
final CommandSender sender = CommandSender.from(uuid);
if (player == null) {
if (sender == null) {
return;
}
message = StringUtils.replace(message, "%player%", player.getName());
message = StringUtils.replace(message, "%player%", sender.getName());
message = ResponseHandler.replaceArgsVariables(message, command);
MessageTranslator.sendActionbar(player, message);
MessageHelper.sendActionbar(sender, message);
}
}
@@ -1,156 +0,0 @@
package de.rayzs.pat.utils.response.action.impl;
import de.rayzs.pat.plugin.logger.Logger;
import de.rayzs.pat.utils.Reflection;
import de.rayzs.pat.utils.StringUtils;
import de.rayzs.pat.utils.message.replacer.PlaceholderReplacer;
import de.rayzs.pat.utils.response.ResponseHandler;
import de.rayzs.pat.utils.response.action.Action;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Objects;
import java.util.UUID;
public class OldBukkitAction implements Action {
private String versionPackage = null;
@Override
public void executeConsoleCommand(String action, UUID uuid, String command, String message) {
Player player = Bukkit.getPlayer(uuid);
message = ResponseHandler.replaceArgsVariables(message, command);
if(player != null) {
message = StringUtils.replace(message, "%player%", player.getName());
}
if (message.contains("%")) {
message = PlaceholderReplacer.replace(player, message);
}
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), message);
}
@Override
public void executePlayerCommand(String action, UUID uuid, String command, String message) {
Player player = Bukkit.getPlayer(uuid);
if (player == null) {
return;
}
message = ResponseHandler.replaceArgsVariables(message, command);
message = StringUtils.replace(message, "%player%", player.getName());
if (message.contains("%")) {
message = PlaceholderReplacer.replace(player, message);
}
Bukkit.dispatchCommand(player, message);
}
@Override
public void sendTitle(String action, UUID uuid, String command, String title, String subTitle, int fadeIn, int stay, int fadeOut) {
Player player = Bukkit.getPlayer(uuid);
if (player == null) {
return;
}
title = StringUtils.replace(title, "&", "§", "%player%", player.getName());
subTitle = StringUtils.replace(title, "&", "§", "%player%", player.getName());
title = ResponseHandler.replaceArgsVariables(title, command);
subTitle = ResponseHandler.replaceArgsVariables(subTitle, command);
title = PlaceholderReplacer.replace(player, title);
subTitle = PlaceholderReplacer.replace(player, subTitle);
player.sendTitle(title, subTitle);
}
@Override
public void addPotionEffect(String action, UUID uuid, String potionEffectTypeName, int duration, int amplifier) {
Player player = Bukkit.getPlayer(uuid);
if (player == null) {
return;
}
PotionEffectType potionEffectType = null;
try {
potionEffectType = PotionEffectType.getByName(potionEffectTypeName);
} catch (Exception ignored) { }
if (potionEffectType == null) {
Logger.warning("! Failed to read action: " + action);
Logger.warning(" > The effect \"" + potionEffectTypeName + "\" could not be found!");
Logger.warning(" > Here's an example of an existing effect:");
Logger.warning(" > BLINDNESS, REGENERATION");
return;
}
player.addPotionEffect(new PotionEffect(potionEffectType, duration, amplifier));
}
@Override
public void playSound(String action, UUID uuid, String soundName, float volume, float pitch) {
Player player = Bukkit.getPlayer(uuid);
if(player == null) {
return;
}
try {
Sound sound = Sound.valueOf(soundName);
player.playSound(player.getLocation(), sound, volume, pitch);
} catch (Exception exception) {
Logger.warning("! Failed to read action: " + action);
Logger.warning(" > The sound \"" + soundName + "\" could not be found!");
Logger.warning(" > Here's an example of an existing sound effect:");
Logger.warning(" > ENTITY_ENDER_DRAGON_GROWL");
}
}
@Override
public void sendActionbar(String action, UUID uuid, String command, String message) {
Player player = Bukkit.getPlayer(uuid);
if (player == null) {
return;
}
message = ResponseHandler.replaceArgsVariables(message, command);
message = StringUtils.replace(message, "&", "§", "%player%", player.getName());
message = PlaceholderReplacer.replace(player, message);
try {
if (versionPackage == null) {
versionPackage = Reflection.getVersionPackageName();
}
Object entityPlayer = player.getClass().getMethod("getHandle").invoke(player);
Constructor<?> constructor = (Objects.<Class<?>>requireNonNull(Class.forName("net.minecraft.server." + versionPackage + ".PacketPlayOutChat"))).getConstructor(Class.forName("net.minecraft.server." + versionPackage + ".IChatBaseComponent"), byte.class);
Object iChatBaseComponent = (Objects.requireNonNull(Class.forName("net.minecraft.server." + versionPackage + ".IChatBaseComponent"))).getDeclaredClasses()[0].getMethod("a", String.class).invoke(null, "{\"text\":\"" + message + "\"}");
Object actionbarPacket = constructor.newInstance(iChatBaseComponent, (byte) 2);
Object playerConnection = entityPlayer.getClass().getField("playerConnection").get(entityPlayer);
playerConnection.getClass().getMethod("sendPacket", Class.forName("net.minecraft.server." + versionPackage + ".Packet")).invoke(playerConnection, actionbarPacket);
} catch (ClassNotFoundException
| NoSuchMethodException
| IllegalAccessException
| InvocationTargetException
| NoSuchFieldException
| InstantiationException exception) {
Logger.warning("! Failed to run action: " + action);
Logger.warning(" > Actionbars are not supported in this server version!");
}
}
}
@@ -1,12 +1,12 @@
package de.rayzs.pat.utils.response.action.impl;
import de.rayzs.pat.plugin.logger.Logger;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.utils.response.ResponseHandler;
import de.rayzs.pat.utils.response.action.Action;
import com.velocitypowered.api.proxy.Player;
import de.rayzs.pat.plugin.VelocityLoader;
import de.rayzs.pat.utils.StringUtils;
import de.rayzs.pat.utils.sender.CommandSender;
import java.util.*;
@@ -14,49 +14,48 @@ public class VelocityAction implements Action {
@Override
public void executeConsoleCommand(String action, UUID uuid, String command, String message) {
Optional<Player> optPlayer = VelocityLoader.getServer().getPlayer(uuid);
final CommandSender sender = CommandSender.from(uuid);
message = ResponseHandler.replaceArgsVariables(message, command);
if (optPlayer.isPresent()) {
message = message.replace("%player%", optPlayer.get().getUsername());
if (sender != null && sender.isPlayer()) {
message = message.replace("%player%", sender.getName());
}
VelocityLoader.getServer().getCommandManager().executeAsync(VelocityLoader.getServer().getConsoleCommandSource(), message);
VelocityLoader.getServer().getCommandManager().executeAsync(
VelocityLoader.getServer().getConsoleCommandSource(),
message
);
}
@Override
public void executePlayerCommand(String action, UUID uuid, String command, String message) {
Optional<Player> optPlayer = VelocityLoader.getServer().getPlayer(uuid);
final CommandSender sender = CommandSender.from(uuid);
if (optPlayer.isEmpty()) {
if (sender == null) {
return;
}
Player player = optPlayer.get();
message = message.replace("%player%", player.getUsername());
message = message.replace("%player%", sender.getName());
message = ResponseHandler.replaceArgsVariables(message, command);
VelocityLoader.getServer().getCommandManager().executeAsync(player, message);
sender.execute(message);
}
@Override
public void sendTitle(String action, UUID uuid, String command, String title, String subTitle, int fadeIn, int stay, int fadeOut) {
Optional<Player> optPlayer = VelocityLoader.getServer().getPlayer(uuid);
final CommandSender sender = CommandSender.from(uuid);
if (optPlayer.isEmpty()) {
if (sender == null) {
return;
}
Player player = optPlayer.get();
title = ResponseHandler.replaceArgsVariables(title, command);
subTitle = ResponseHandler.replaceArgsVariables(subTitle, command);
title = StringUtils.replace(title, "%player%", player.getUsername());
subTitle = StringUtils.replace(subTitle, "%player%", player.getUsername());
title = StringUtils.replace(title, "%player%", sender.getName());
subTitle = StringUtils.replace(subTitle, "%player%", sender.getName());
MessageTranslator.sendTitle(player, title, subTitle, fadeIn, stay, fadeOut);
MessageHelper.sendTitle(sender, title, subTitle, fadeIn, stay, fadeOut);
}
@Override
@@ -65,16 +64,14 @@ public class VelocityAction implements Action {
@Override
public void playSound(String action, UUID uuid, String soundName, float volume, float pitch) {
Optional<Player> optPlayer = VelocityLoader.getServer().getPlayer(uuid);
final CommandSender sender = CommandSender.from(uuid);
if (optPlayer.isEmpty()) {
if (sender == null) {
return;
}
Player player = optPlayer.get();
try {
MessageTranslator.playSound(player, soundName, volume, pitch);
MessageHelper.playSound(sender, soundName, volume, pitch);
} catch (Exception exception) {
Logger.warning("! Failed to read action: " + action);
Logger.warning(" > The sound \"" + soundName + "\" could not be found!");
@@ -85,17 +82,15 @@ public class VelocityAction implements Action {
@Override
public void sendActionbar(String action, UUID uuid, String command, String message) {
Optional<Player> optPlayer = VelocityLoader.getServer().getPlayer(uuid);
final CommandSender sender = CommandSender.from(uuid);
if (optPlayer.isEmpty()) {
if (sender == null) {
return;
}
Player player = optPlayer.get();
message = ResponseHandler.replaceArgsVariables(message, command);
message = StringUtils.replace(message, "%player%", player.getUsername());
message = StringUtils.replace(message, "%player%", sender.getName());
MessageTranslator.sendActionbar(player, message);
MessageHelper.sendActionbar(sender, message);
}
}
@@ -51,5 +51,9 @@ public interface CommandSender {
String getName();
String getServerName();
void execute(String command);
void sendTitle(String title, String subtitle, int fadeIn, int stay, int fadeOut);
void sendActionbar(String message);
void sendMessage(String message);
}
@@ -1,13 +1,20 @@
package de.rayzs.pat.utils.sender.impl;
import de.rayzs.pat.api.storage.Storage;
import de.rayzs.pat.plugin.logger.Logger;
import de.rayzs.pat.utils.Reflection;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.utils.message.replacer.PlaceholderReplacer;
import de.rayzs.pat.utils.sender.CommandSenderAbstract;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Objects;
import java.util.UUID;
public class BukkitSender extends CommandSenderAbstract {
@@ -100,23 +107,95 @@ public class BukkitSender extends CommandSenderAbstract {
@Override
public void sendMessage(String message) {
if (MessageTranslator.isSupported()) {
MessageTranslator.send(sender, message);
if (MessageHelper.isSupported()) {
MessageHelper.sendMessage(this, message);
return;
}
// To prevent color-code overlapping on Bukkit consoles.
if (Reflection.isCraftbukkit() && isConsole()) {
if (Reflection.isBukkit() && isConsole()) {
String[] lines = message.split("\n");
for (String line : lines) {
sender.sendMessage(MessageTranslator.replaceMessage(sender, line));
sender.sendMessage(PlaceholderReplacer.applyPlaceholders(this, line));
}
return;
}
sender.sendMessage(MessageTranslator.replaceMessage(sender, message));
sender.sendMessage(PlaceholderReplacer.applyPlaceholders(this, message));
}
@Override
public void sendTitle(String title, String subtitle, int fadeIn, int stay, int fadeOut) {
if (isPlayer()) return;
if (MessageHelper.isSupported()) {
MessageHelper.sendTitle(this, title, subtitle, 10, 10, 10);
return;
}
if (Reflection.isAfter(1, 11, 0)) {
((Player) sender).sendTitle(
PlaceholderReplacer.applyPlaceholders(this, title),
PlaceholderReplacer.applyPlaceholders(this, subtitle),
fadeIn, stay, fadeOut
);
} else {
((Player) sender).sendTitle(
PlaceholderReplacer.applyPlaceholders(this, title),
PlaceholderReplacer.applyPlaceholders(this, subtitle)
);
}
}
@Override
public void execute(String command) {
Bukkit.dispatchCommand(sender, command);
}
@Override
public void sendActionbar(String message) {
if (MessageHelper.isSupported()) {
MessageHelper.sendActionbar(this, message);
return;
}
if (Reflection.isAtMost(1, 16)) {
try {
final Object entityPlayer = sender.getClass().getMethod("getHandle").invoke(sender);
final Constructor<?> constructor = (Objects.<Class<?>> requireNonNull(Class.forName("net.minecraft.server." + Reflection.getVersionPackageName() + ".PacketPlayOutChat")))
.getConstructor(
Class.forName("net.minecraft.server." + Reflection.getVersionPackageName() + ".IChatBaseComponent"),
byte.class
);
final Object iChatBaseComponent = (Objects.requireNonNull(Class.forName("net.minecraft.server." + Reflection.getVersionPackageName() + ".IChatBaseComponent")))
.getDeclaredClasses()[0]
.getMethod("a", String.class)
.invoke(null, "{\"text\":\"" + message + "\"}");
final Object actionbarPacket = constructor.newInstance(iChatBaseComponent, (byte) 2);
final Object playerConnection = entityPlayer.getClass().getField("playerConnection").get(entityPlayer);
playerConnection.getClass().getMethod(
"sendPacket",
Class.forName("net.minecraft.server." + Reflection.getVersionPackageName() + ".Packet")
).invoke(playerConnection, actionbarPacket);
return;
} catch (ClassNotFoundException
| NoSuchMethodException
| IllegalAccessException
| InvocationTargetException
| NoSuchFieldException
| InstantiationException ignored) { }
}
Logger.warning("Actionbars are not supported in this server version!");
}
@Override
@@ -1,10 +1,14 @@
package de.rayzs.pat.utils.sender.impl;
import de.rayzs.pat.api.storage.Storage;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.plugin.logger.Logger;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.utils.message.replacer.PlaceholderReplacer;
import de.rayzs.pat.utils.sender.CommandSenderAbstract;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import java.util.UUID;
@@ -102,12 +106,56 @@ public class BungeeSender extends CommandSenderAbstract {
@Override
public void sendMessage(String message) {
if (MessageTranslator.isSupported()) {
MessageTranslator.send(sender, message);
if (MessageHelper.isSupported()) {
MessageHelper.sendMessage(this, message);
return;
}
sender.sendMessage(MessageTranslator.replaceMessage(sender, message));
sender.sendMessage(PlaceholderReplacer.applyPlaceholders(this, message));
}
@Override
public void sendTitle(String title, String subtitle, int fadeIn, int stay, int fadeOut) {
if (MessageHelper.isSupported()) {
MessageHelper.sendTitle(this, title, subtitle, fadeIn, stay, fadeOut);
return;
}
if (! (sender instanceof ProxiedPlayer player)) {
return;
}
ProxyServer.getInstance().createTitle()
.title(TextComponent.fromLegacyText(PlaceholderReplacer.applyPlaceholders(this, title)))
.subTitle(TextComponent.fromLegacyText(PlaceholderReplacer.applyPlaceholders(this, subtitle)))
.fadeIn(fadeIn)
.stay(stay)
.fadeOut(fadeOut)
.send(player);
}
@Override
public void sendActionbar(String message) {
if (MessageHelper.isSupported()) {
MessageHelper.sendActionbar(this, message);
return;
}
if (! (sender instanceof ProxiedPlayer player)) {
return;
}
player.sendMessage(
ChatMessageType.ACTION_BAR,
TextComponent.fromLegacyText(
PlaceholderReplacer.applyPlaceholders(this, message)
)
);
}
@Override
public void execute(String command) {
ProxyServer.getInstance().getPluginManager().dispatchCommand(sender, command);
}
@Override
@@ -4,7 +4,7 @@ import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import de.rayzs.pat.api.storage.Storage;
import de.rayzs.pat.plugin.VelocityLoader;
import de.rayzs.pat.utils.message.MessageTranslator;
import de.rayzs.pat.utils.message.MessageHelper;
import de.rayzs.pat.utils.sender.CommandSenderAbstract;
import java.util.Optional;
@@ -105,7 +105,22 @@ public class VelocitySender extends CommandSenderAbstract {
@Override
public void sendMessage(String message) {
MessageTranslator.send(sender, message);
MessageHelper.sendMessage(this, message);
}
@Override
public void sendTitle(String title, String subtitle, int fadeIn, int stay, int fadeOut) {
MessageHelper.sendTitle(this, title, subtitle, fadeIn, stay, fadeOut);
}
@Override
public void sendActionbar(String message) {
MessageHelper.sendActionbar(this, message);
}
@Override
public void execute(String command) {
VelocityLoader.getServer().getCommandManager().executeAsync(sender, command);
}
@Override
+8 -2
View File
@@ -3,7 +3,13 @@ description: Hides more than just your plugins.
website: https://www.rayzs.de/products/proantitab/page
author: Rayzs_YT
version: 2.5.0
version: 2.5.1
main: de.rayzs.pat.plugin.BungeeLoader
softDepends: [LuckPerms, PAPIProxyBridge]
softDepends: [LuckPerms, PAPIProxyBridge]
libraries:
- net.kyori:adventure-platform-bungeecord:4.4.1
- net.kyori:adventure-text-minimessage:5.2.0
- net.kyori:adventure-api:5.2.0
- net.kyori:adventure-text-serializer-legacy:5.2.0
@@ -15,7 +15,7 @@
# %pat_general_prefix% = Get the PAT prefix set in the config.yml.
# %pat_general_user% = Get the name of the user who receives this message.
# %pat_general_version_current% = Get current version name of PAT.
# %pat_general_version_newest% = Get newest version name of PAT.
# %pat_general_version_newest% = Get the newest version name of PAT.
# Placeholders of some of the messages in the config.yml:
# %pat_message_unknowncommand% = Get the "unknown command"-message from the config.yml of PAT. [cancel-blocked-command]
@@ -64,6 +64,11 @@ allow-group-overruling: false
# lpv (...) permission set proantitab.group.<group> server=<server>
update-groups-per-server: false
# If you whitelist a command using PAT that does not exist, it would still
# make the command tab-completable, even though it doesn't exist.
# (!) WARNING: Only works with PAT groups!
tab-completion-for-not-existing-commands: false
# Enabling this forwards the notification message to the backend
# server so it will be displayed there as well. (Only in console)
forward-console-notification-alerts: false
+7 -1
View File
@@ -3,13 +3,19 @@ description: Hides more than just your plugins.
website: https://www.rayzs.de/products/proantitab/page
author: Rayzs_YT
version: 2.5.0
version: 2.5.1
api-version: 1.13
folia-supported: true
main: de.rayzs.pat.plugin.BukkitLoader
softdepend: [PlaceholderAPI, LuckPerms, GroupManager, Skript, ViaVersion, ViaBackwards, ViaRewind]
libraries:
- net.kyori:adventure-platform-bukkit:4.4.1
- net.kyori:adventure-text-minimessage:5.2.0
- net.kyori:adventure-api:5.2.0
- net.kyori:adventure-text-serializer-legacy:5.2.0
commands:
proantitab:
aliases: [pat]
+19
View File
@@ -0,0 +1,19 @@
{
"id": "proantitab",
"name": "ProAntiTab",
"version": "2.5.1",
"description": "Hides more than just your plugins.",
"url": "https://www.rayzs.de/products/proantitab/page",
"authors": ["Rayzs_YT"],
"main":"de.rayzs.pat.plugin.VelocityLoader",
"dependencies": [
{
"id":"luckperms",
"optional":true
},
{
"id":"papiproxybridge",
"optional":true
}
]
}
@@ -0,0 +1,371 @@
package de.rayzs.pat.utils;
import de.rayzs.pat.api.storage.Storage;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import de.rayzs.pat.utils.configuration.helper.MultipleMessagesHelper;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.nio.charset.StandardCharsets;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.*;
class CommunicationPacketsSecurityTest {
private static final String TOKEN = "test-token-6d21af0b-e44e-4c94-9920-7ecbd96243e1";
private static final UUID BACKEND_ID = UUID.fromString("28ef48c0-62cc-4f25-b2f0-6fb53d2dd61b");
private static final UUID OTHER_BACKEND_ID = UUID.fromString("dfe05064-55c9-4b3b-a65f-d6beaeb0b9af");
private static final String EXPECTED_HKDF_SALT = "ProAntiTab/plugin-messaging/v1";
private String previousToken;
@BeforeEach
void setUp() {
previousToken = Storage.TOKEN;
Storage.TOKEN = TOKEN;
}
@AfterEach
void tearDown() {
Storage.TOKEN = previousToken;
}
@Test
void normalPacketRoundTripsWithMatchingBackendContext() {
UUID playerId = UUID.fromString("7c9f9835-cf72-49c1-aaf0-e4c9853cf7fa");
var original = new CommunicationPackets.Proxy2Backend.ExecutePlayerCommandPacket(
playerId, "warp spawn"
);
byte[] encoded = CommunicationPackets.preparePacket(original, BACKEND_ID);
Object decoded = CommunicationPackets.readPacket(encoded, BACKEND_ID);
assertNotNull(encoded);
assertEquals(original, decoded);
assertTrue(CommunicationPackets.isP2BPacket(decoded));
assertFalse(CommunicationPackets.isInitialPacket(decoded));
}
@Test
void initialHandshakePacketRoundTripsIndependentlyOfBackendContext() {
var original = new CommunicationPackets.Backend2Proxy.IdentityRequestPacket(BACKEND_ID);
byte[] encoded = CommunicationPackets.preparePacket(original, BACKEND_ID);
assertNotNull(encoded);
assertTrue(CommunicationPackets.isInitialPacket(encoded));
assertEquals(original, CommunicationPackets.readPacket(encoded, OTHER_BACKEND_ID));
}
@Test
void samePlaintextProducesDifferentCiphertextBecauseNonceIsRandom() {
var packet = new CommunicationPackets.Proxy2Backend.ConsoleMessagePacket("same message");
byte[] first = CommunicationPackets.preparePacket(packet, BACKEND_ID);
byte[] second = CommunicationPackets.preparePacket(packet, BACKEND_ID);
assertNotNull(first);
assertNotNull(second);
assertFalse(Arrays.equals(first, second), "AES-GCM packets must use fresh nonces");
assertEquals(packet, CommunicationPackets.readPacket(first, BACKEND_ID));
assertEquals(packet, CommunicationPackets.readPacket(second, BACKEND_ID));
}
@Test
void tamperingWithCiphertextIsRejected() {
var packet = new CommunicationPackets.Proxy2Backend.ExecuteConsoleCommandPacket("say secure");
byte[] encoded = CommunicationPackets.preparePacket(packet, BACKEND_ID);
assertNotNull(encoded);
byte[] tampered = encoded.clone();
tampered[tampered.length - 1] ^= 0x01;
assertNull(CommunicationPackets.readPacket(tampered, BACKEND_ID));
}
@Test
void tamperingWithAuthenticatedHeaderIsRejected() {
var packet = new CommunicationPackets.Proxy2Backend.KeepAliveResponsePacket();
byte[] encoded = CommunicationPackets.preparePacket(packet, BACKEND_ID);
assertNotNull(encoded);
byte[] tampered = encoded.clone();
tampered[1] = 0; // claim this normal packet is an initial packet
assertNull(CommunicationPackets.readPacket(tampered, BACKEND_ID));
}
@Test
void wrongBackendUuidCannotDecryptNormalPacket() {
var packet = new CommunicationPackets.Backend2Proxy.AnnouncePlayerPermissionChanges(UUID.randomUUID());
byte[] encoded = CommunicationPackets.preparePacket(packet, BACKEND_ID);
assertNotNull(encoded);
assertNull(CommunicationPackets.readPacket(encoded, OTHER_BACKEND_ID));
assertEquals(packet, CommunicationPackets.readPacket(encoded, BACKEND_ID));
}
@Test
void wrongStorageTokenCannotDecryptPacket() {
var packet = new CommunicationPackets.Proxy2Backend.NotificationPacket(
UUID.randomUUID(), "help"
);
byte[] encoded = CommunicationPackets.preparePacket(packet, BACKEND_ID);
assertNotNull(encoded);
Storage.TOKEN = "different-token-that-must-not-authenticate";
assertNull(CommunicationPackets.readPacket(encoded, BACKEND_ID));
}
@Test
void truncatedAndInvalidFramesAreRejectedWithoutThrowing() {
assertDoesNotThrow(() -> {
assertNull(CommunicationPackets.readPacket(null, BACKEND_ID));
assertNull(CommunicationPackets.readPacket(new byte[0], BACKEND_ID));
assertNull(CommunicationPackets.readPacket(new byte[8], BACKEND_ID));
});
var packet = new CommunicationPackets.Backend2Proxy.KeepAlivePacket();
byte[] encoded = CommunicationPackets.preparePacket(packet, BACKEND_ID);
assertNotNull(encoded);
for (int length : new int[]{1, 2, 5, 13, encoded.length - 1}) {
byte[] truncated = Arrays.copyOf(encoded, length);
assertDoesNotThrow(() -> assertNull(CommunicationPackets.readPacket(truncated, BACKEND_ID)));
}
}
@Test
void invalidProtocolVersionIsRejected() {
var packet = new CommunicationPackets.Backend2Proxy.KeepAlivePacket();
byte[] encoded = CommunicationPackets.preparePacket(packet, BACKEND_ID);
assertNotNull(encoded);
byte[] wrongVersion = encoded.clone();
wrongVersion[0] = (byte) (CommunicationPackets.PROTOCOL_VERSION + 1);
assertNull(CommunicationPackets.readPacket(wrongVersion, BACKEND_ID));
}
@Test
void blankTokenDisablesEncodingAndDecoding() {
var packet = new CommunicationPackets.Backend2Proxy.KeepAlivePacket();
byte[] valid = CommunicationPackets.preparePacket(packet, BACKEND_ID);
assertNotNull(valid);
Storage.TOKEN = " ";
assertNull(CommunicationPackets.preparePacket(packet, BACKEND_ID));
assertNull(CommunicationPackets.readPacket(valid, BACKEND_ID));
}
@Test
void representativePacketTypesPreserveTheirPayloads() {
UUID player = UUID.fromString("c112a6b7-82ec-4120-a4f6-f7c6d0953eef");
Set<UUID> players = new HashSet<>(Set.of(
player,
UUID.fromString("f59bad95-cd4c-4fc2-a76b-834f38de2447")
));
CommunicationPackets.PATPacket[] packets = {
new CommunicationPackets.Proxy2Backend.UpdatePacket(null),
new CommunicationPackets.Proxy2Backend.UpdatePacket(player),
new CommunicationPackets.Proxy2Backend.IdentityPacket(BACKEND_ID, "survival"),
new CommunicationPackets.Proxy2Backend.ExecuteConsoleCommandPacket("list"),
new CommunicationPackets.Proxy2Backend.ConsoleMessagePacket("hello"),
new CommunicationPackets.Backend2Proxy.IdentityResponsePacket(BACKEND_ID),
new CommunicationPackets.Backend2Proxy.DataSyncReceivedPacket(),
new CommunicationPackets.Backend2Proxy.AnnounceGroupPermissionChanges(players)
};
for (CommunicationPackets.PATPacket packet : packets) {
byte[] encoded = CommunicationPackets.preparePacket(packet, BACKEND_ID);
assertNotNull(encoded, () -> "Failed to encode " + packet.getClass().getSimpleName());
assertEquals(packet, CommunicationPackets.readPacket(encoded, BACKEND_ID),
() -> "Round-trip mismatch for " + packet.getClass().getSimpleName());
}
}
@Test
void normalPacketRequiresBackendContext() {
var packet = new CommunicationPackets.Backend2Proxy.KeepAlivePacket();
assertNull(CommunicationPackets.preparePacket(packet, null));
byte[] encoded = CommunicationPackets.preparePacket(packet, BACKEND_ID);
assertNotNull(encoded);
assertNull(CommunicationPackets.readPacket(encoded, null));
}
private static CommunicationPackets.Proxy2Backend.DataSyncPacket sync(String prefix, List<String> lines) {
return new CommunicationPackets.Proxy2Backend.DataSyncPacket(
new CommunicationPackets.Proxy2Backend.DataSyncPacket.Messages(prefix),
new CommunicationPackets.Proxy2Backend.DataSyncPacket.AutoLowerCase(true),
new CommunicationPackets.Proxy2Backend.DataSyncPacket.UnknownCommand(true, new MultipleMessagesHelper(lines)));
}
private static void assertRoundTrip(CommunicationPackets.PATPacket packet) {
byte[] wire = CommunicationPackets.preparePacket(packet, BACKEND_ID);
assertNotNull(wire, () -> "Encoding failed for " + packet.getClass().getSimpleName());
Object decoded = CommunicationPackets.readPacket(wire, BACKEND_ID);
if (packet instanceof CommunicationPackets.Proxy2Backend.DataSyncPacket original) {
var actual = assertInstanceOf(CommunicationPackets.Proxy2Backend.DataSyncPacket.class, decoded);
assertEquals(original.messages(), actual.messages());
assertEquals(original.autoLowerCase(), actual.autoLowerCase());
assertEquals(original.unknownCommand().enabled(), actual.unknownCommand().enabled());
assertEquals(original.unknownCommand().message().getLines(), actual.unknownCommand().message().getLines());
} else {
assertEquals(packet, decoded);
}
}
@Test
void allFourteenPacketTypesRoundTrip() {
UUID player = UUID.fromString("f5b80b7d-c1ad-48f0-b07b-4bd3e24fb8f2");
CommunicationPackets.PATPacket[] packets = {
new CommunicationPackets.Proxy2Backend.UpdatePacket(null),
new CommunicationPackets.Proxy2Backend.IdentityPacket(BACKEND_ID, "survival"),
new CommunicationPackets.Proxy2Backend.NotificationPacket(player, "help"),
new CommunicationPackets.Proxy2Backend.ExecuteConsoleCommandPacket("list"),
new CommunicationPackets.Proxy2Backend.ExecutePlayerCommandPacket(player, "spawn"),
new CommunicationPackets.Proxy2Backend.ConsoleMessagePacket("hello"),
new CommunicationPackets.Proxy2Backend.KeepAliveResponsePacket(),
sync("prefix", List.of("one", "two", "three")),
new CommunicationPackets.Backend2Proxy.IdentityRequestPacket(BACKEND_ID),
new CommunicationPackets.Backend2Proxy.IdentityResponsePacket(BACKEND_ID),
new CommunicationPackets.Backend2Proxy.DataSyncReceivedPacket(),
new CommunicationPackets.Backend2Proxy.KeepAlivePacket(),
new CommunicationPackets.Backend2Proxy.AnnouncePlayerPermissionChanges(player),
new CommunicationPackets.Backend2Proxy.AnnounceGroupPermissionChanges(Set.of(player, BACKEND_ID))
};
assertEquals(14, packets.length);
for (var packet : packets) assertRoundTrip(packet);
}
@Test
void maximumPlaintextAndWireSizeAreAcceptedAndNextByteRejected() {
// 19 bytes of framing + 16,384 + 8,192 + 8,142 bytes of strings = 32,737 bytes.
var maximum = sync("a".repeat(16384), List.of("b".repeat(8192), "c".repeat(8142)));
byte[] wire = CommunicationPackets.preparePacket(maximum, BACKEND_ID);
assertNotNull(wire);
assertEquals(32_767, wire.length);
assertRoundTrip(maximum);
assertNull(CommunicationPackets.readPacket(Arrays.copyOf(wire, wire.length + 1), BACKEND_ID));
var overMaximum = sync("a".repeat(16384), List.of("b".repeat(8192), "c".repeat(8143)));
assertNull(CommunicationPackets.preparePacket(overMaximum, BACKEND_ID));
}
@Test
void utf8StringsAreLimitedByTotalPacketBytes() {
// 1-byte type + 4-byte length + UTF-8 bytes.
assertRoundTrip(new CommunicationPackets.Proxy2Backend.ConsoleMessagePacket("é".repeat(16_366)));
assertNull(CommunicationPackets.preparePacket(
new CommunicationPackets.Proxy2Backend.ConsoleMessagePacket("é".repeat(16_367)), BACKEND_ID));
}
@Test
void collectionsAreLimitedByTotalPacketBytes() {
// Each empty string still takes 4 bytes for its length prefix.
assertRoundTrip(sync("", java.util.Collections.nCopies(8_181, "")));
assertNull(CommunicationPackets.preparePacket(
sync("", java.util.Collections.nCopies(8_182, "")), BACKEND_ID));
Set<UUID> ids = new HashSet<>();
for (int i = 0; i < 2_045; i++) ids.add(new UUID(0, i));
assertRoundTrip(new CommunicationPackets.Backend2Proxy.AnnounceGroupPermissionChanges(ids));
ids.add(new UUID(0, 2_045));
assertNull(CommunicationPackets.preparePacket(
new CommunicationPackets.Backend2Proxy.AnnounceGroupPermissionChanges(ids), BACKEND_ID));
}
// Independently encrypt arbitrary plaintext to exercise malformed authenticated codec input.
private static byte[] authenticatedFrame(byte[] plaintext, boolean initial) throws Exception {
Method derive = CommunicationPackets.class.getDeclaredMethod("deriveKey", String.class, UUID.class);
derive.setAccessible(true);
SecretKeySpec key = (SecretKeySpec) derive.invoke(null, TOKEN, initial ? null : BACKEND_ID);
byte[] header = {CommunicationPackets.PROTOCOL_VERSION, (byte) (initial ? 0 : 1)};
byte[] nonce = new byte[12]; // Deterministic nonce used only by isolated test frames.
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(128, nonce));
cipher.updateAAD(header);
byte[] ciphertext = cipher.doFinal(plaintext);
byte[] frame = new byte[header.length + nonce.length + ciphertext.length];
System.arraycopy(header, 0, frame, 0, 2);
System.arraycopy(nonce, 0, frame, 2, 12);
System.arraycopy(ciphertext, 0, frame, 14, ciphertext.length);
return frame;
}
private static byte[] plaintext(int type, int length) throws Exception {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(bytes);
out.writeByte(type);
out.writeInt(length);
return bytes.toByteArray();
}
@Test
void authenticatedMalformedPayloadsAreRejected() throws Exception {
// Unknown packet type, trailing bytes, invalid/negative string lengths.
byte[][] invalid = {
{(byte) 127},
{23, 1},
plaintext(6, -1),
plaintext(6, 32_738),
plaintext(6, 10),
plaintext(25, -1),
plaintext(25, Integer.MAX_VALUE),
plaintext(8, -1),
plaintext(8, Integer.MAX_VALUE)
};
for (byte[] payload : invalid) {
assertNull(CommunicationPackets.readPacket(authenticatedFrame(payload, false), BACKEND_ID),
"Malformed authenticated packet accepted: " + Arrays.toString(payload));
}
// Initial flag must match decoded packet type, even with a valid GCM tag.
assertNull(CommunicationPackets.readPacket(authenticatedFrame(new byte[]{23}, true), BACKEND_ID));
assertNull(CommunicationPackets.readPacket(authenticatedFrame(new byte[]{20}, false), BACKEND_ID));
}
@Test
void hkdfMatchesIndependentExtractExpandComputation() throws Exception {
Method derive = CommunicationPackets.class.getDeclaredMethod("deriveKey", String.class, UUID.class);
derive.setAccessible(true);
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(EXPECTED_HKDF_SALT.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] prk = mac.doFinal(TOKEN.getBytes(StandardCharsets.UTF_8));
for (UUID context : new UUID[]{null, BACKEND_ID}) {
String info = context == null ? "initial" : "backend:" + context;
mac.init(new SecretKeySpec(prk, "HmacSHA256"));
mac.update(info.getBytes(StandardCharsets.UTF_8));
mac.update((byte) 1);
byte[] expected = mac.doFinal();
SecretKeySpec actual = (SecretKeySpec) derive.invoke(null, TOKEN, context);
assertArrayEquals(expected, actual.getEncoded());
}
}
@Test
void oversizedWirePacketIsRejectedBeforeDecryption() {
// Default Velocity maximum payload size plus one byte.
byte[] oversized = new byte[32_768];
oversized[0] = CommunicationPackets.PROTOCOL_VERSION;
oversized[1] = 1;
assertNull(CommunicationPackets.readPacket(oversized, BACKEND_ID));
}
}