Compare commits

...
31 Commits
Author SHA1 Message Date
Dan Mulloy 39808bc64f wip: dedicated paper plugin 2025-08-21 21:34:27 -05:00
Dan Mulloy 771a530fe2 Fix CME on disable
Fixes #3544
2025-08-16 22:38:57 -05:00
Diogo Correia ca9b5bbb80 fix: use RegistryOps when (de)serializing chat component on 1.21.6+ (#3546)
This fixes an error during serialization when a hover event has an item
with `components` (e.g., enchantments).
2025-08-13 01:13:43 +00:00
NotSecureFly d790ded81f Use the correct reflection method in setTypeAndData method, and rename GET_BLOCK_DATA to DEFAULT_BLOCK_DATA for clarity (#3530) 2025-08-13 01:10:44 +00:00
Tim203 f357ac85d0 Preserve the pipeline order when async receiving packets (#3351) 2025-08-13 01:07:29 +00:00
Dan Mulloy 66cdb5b69d Move dev builds from Jenkins to GitHub (#3542) 2025-08-10 16:38:54 -05:00
Dan Mulloy 1aeb002afe Update version to 5.4.0 for release 2025-08-06 20:25:03 -05:00
Dan Mulloy 79d44f0d6e Minor build fixes 2025-08-06 20:06:30 -05:00
Dan Mulloy 9bde9b4231 Update group in Gradle example 2025-08-06 11:58:30 -05:00
Dan Mulloy 714252df6f Publish to Maven Central 2025-08-06 11:04:00 -05:00
Leviaria e56bcf624d Added support for Minecraft 1.21.8 (#3523) 2025-07-27 19:30:42 -05:00
Diogo Correia 596e5e4246 fix: incorrect version gating on WrappedTeamParamters (#3496) 2025-06-27 19:22:06 -05:00
Diogo Correia f3f895995a fix: initialization error of ChunkData wrapper on MC 1.21.5+ (#3495) 2025-06-27 12:23:21 -05:00
Diogo Correia 67e7375b2f fix: initialization error for WrappedChatComponent on 1.20.4-1.21.5 (#3493) 2025-06-27 10:17:03 -05:00
Pasqual Koschmieder 9417eee444 drop unnecessary minecraft import from EnumWrappers (#3476) 2025-06-19 12:57:42 -05:00
Dan Mulloy a432fae4dd 1.21.6 update (#3475) 2025-06-18 15:33:20 -05:00
JavaFruit 8a5e59d4f2 fix: re-add string overloads for nametagVisibility and collisionRule in WrappedTeamParameters.Builder (#3445) 2025-05-23 20:03:53 +02:00
Maurice Eisenblätter 90a386b743 Fix some 1.21.5 related issues (unit tests) (#3432) 2025-05-09 19:07:58 -05:00
Pasqual Koschmieder a80762feed update dependencies for java 24 support (#3425) 2025-05-06 14:07:29 -05:00
Brokkonaut a936183b76 Fix WrappedList - NBT lists may now be heterogeneous (#3394) 2025-04-22 19:21:43 -05:00
Pasqual Koschmieder 804aa5df11 fix: data watcher serializer initialization (#3384) 2025-03-31 09:46:08 -05:00
Maurice Eisenblätter 1e73df8600 Initial support for 1.21.5 (#3381) 2025-03-29 17:26:16 -05:00
Ryan 4314a55866 fix: Show helpful message when no supported updater loads (#3358) 2025-03-25 19:37:11 -05:00
Diogo Correia 2c0d632dc2 fix: listOrder not preserved when converting PlayerInfoData (#3347)
The field `listOrder` was always set to zero in the wrapper instead of
being read from the NMS object. This commit fixes that, while adding the
respective constructor and getter.
2025-02-04 01:11:03 +00:00
Pasqual Koschmieder fd5d8c576e fix/improve publishing (#3350) 2025-02-03 19:03:37 -06:00
Diogo Correia 0256953056 fix: unable to convert PlayerInfoData if gamemode is null (#3334) 2024-12-30 19:30:26 -06:00
Dan Mulloy 24262d3f7d Update link to wiki.vg 2024-12-09 14:41:23 -06:00
Brokkonaut 7a85663ded Update to 1.21.4 (#3319) 2024-12-09 14:17:17 -06:00
Dan Mulloy bcff6c6719 Update to 1.21.3 2024-11-08 11:03:51 -06:00
Dan Mulloy 3155e76d06 Mojang Mappings 2024-10-27 21:33:15 -05:00
Dan Mulloy 6845acd89d Update to 1.21.3 2024-10-26 15:21:23 -05:00
125 changed files with 3237 additions and 1689 deletions

No files matched your search

+4 -2
View File
@@ -1,8 +1,10 @@
name: ProtocolLib full build lifecycle
on:
- push
- pull_request
push:
branches-ignore:
- 'gh-pages'
pull_request:
jobs:
build:
+43
View File
@@ -0,0 +1,43 @@
name: ProtocolLib development build
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'
cache: 'gradle'
- name: Run gradle build lifecycle
run: |
COMMIT_SHA=${{ github.sha }} ./gradlew build shadowJar --no-daemon
- name: Upload plugin file
uses: actions/upload-artifact@v4
with:
name: ProtocolLib
path: build/libs/ProtocolLib.jar
- name: Create or update GitHub pre-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
COMMIT_MSG=$(git log -1 --pretty=%B)
SHORT_HASH=$(echo "${{ github.sha }}" | cut -c1-7)
RELEASE_NOTES=$(gh release view dev-build --repo ${{ github.repository }} --json body -q ".body" 2>/dev/null || echo "")
gh release view dev-build --repo ${{ github.repository }} || \
gh release create dev-build --prerelease --title "Development Build" --notes "## Changelog" --repo ${{ github.repository }}
NEW_NOTES="$RELEASE_NOTES"$'\n'"* [$SHORT_HASH] $COMMIT_MSG"
gh release edit dev-build --notes "$NEW_NOTES" --repo ${{ github.repository }}
gh release upload dev-build build/libs/ProtocolLib.jar#ProtocolLib.jar --clobber --repo ${{ github.repository }}
-1
View File
@@ -181,4 +181,3 @@ logs/
build/
logs/
.java-version
gradle.properties
+10 -15
View File
@@ -2,7 +2,7 @@
Certain tasks are impossible to perform with the standard Bukkit API, and may require
working with and even modifying Minecraft directly. A common technique is to modify incoming
and outgoing [packets](https://wiki.vg/Protocol), or inject custom packets into the
and outgoing packets, or inject custom packets into the
stream. This is quite cumbersome to do, however, and most implementations will break
as soon as a new version of Minecraft has been released, mostly due to obfuscation.
@@ -12,9 +12,11 @@ lead to more subtle bugs.
### Resources
* [Resource Page](https://www.spigotmc.org/resources/protocollib.1997/)
* [Dev Builds](https://ci.dmulloy2.net/job/ProtocolLib)
* [JavaDoc](https://ci.dmulloy2.net/job/ProtocolLib/javadoc/index.html)
* [Spigot Page](https://spigotmc.org/resources/protocollib.1997/)
* [Releases](https://github.com/dmulloy2/ProtocolLib/releases)
* [Dev Build](https://github.com/dmulloy2/ProtocolLib/releases/tag/dev-build)
* [JavaDoc](https://dmulloy2.net/ProtocolLib/javadoc/)
* [Protocol Wiki](https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Protocol)
### Compilation
@@ -42,18 +44,11 @@ depend: [ ProtocolLib ]
You can also add ProtocolLib as a Maven dependency:
````xml
<repositories>
<repository>
<id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/repository/public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.comphenix.protocol</groupId>
<groupId>net.dmulloy2</groupId>
<artifactId>ProtocolLib</artifactId>
<version>5.1.0</version>
<version>5.4.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
@@ -63,11 +58,11 @@ Or use the maven dependency with gradle:
```gradle
repositories {
maven { url "https://repo.dmulloy2.net/repository/public/" }
mavenCentral()
}
dependencies {
compileOnly 'com.comphenix.protocol:ProtocolLib:5.1.0'
compileOnly 'net.dmulloy2:ProtocolLib:5.4.0'
}
```
-130
View File
@@ -1,130 +0,0 @@
plugins {
id 'java-library'
id 'maven-publish'
id 'com.gradleup.shadow' version '8.3.2'
}
group = 'com.comphenix.protocol'
version = '5.3.0'
description = 'Provides access to the Minecraft protocol'
def isSnapshot = version.endsWith('-SNAPSHOT')
repositories {
// mavenLocal() // can speed up build, but may fail in CI
mavenCentral()
maven {
url 'https://repo.dmulloy2.net/repository/public/'
}
maven {
url 'https://hub.spigotmc.org/nexus/content/groups/public/'
}
maven {
url 'https://libraries.minecraft.net/'
metadataSources {
mavenPom()
artifact()
ignoreGradleMetadataRedirection()
}
}
}
dependencies {
implementation 'net.bytebuddy:byte-buddy:1.15.1'
compileOnly 'org.spigotmc:spigot-api:1.21.1-R0.1-SNAPSHOT'
compileOnly 'org.spigotmc:spigot:1.21.1-R0.1-SNAPSHOT'
compileOnly 'io.netty:netty-all:4.0.23.Final'
compileOnly 'net.kyori:adventure-text-serializer-gson:4.14.0'
compileOnly 'com.googlecode.json-simple:json-simple:1.1.1'
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.10.0'
testImplementation 'org.mockito:mockito-core:5.6.0'
testImplementation 'io.netty:netty-common:4.1.97.Final'
testImplementation 'io.netty:netty-transport:4.1.97.Final'
testImplementation 'org.spigotmc:spigot:1.21.1-R0.1-SNAPSHOT'
testImplementation 'net.kyori:adventure-text-serializer-gson:4.14.0'
testImplementation 'net.kyori:adventure-text-serializer-plain:4.14.0'
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
withJavadocJar()
withSourcesJar()
}
jar {
manifest {
attributes(
'paperweight-mappings-namespace': 'mojang'
)
}
}
shadowJar {
dependencies {
include(dependency('net.bytebuddy:byte-buddy:.*'))
}
relocate 'net.bytebuddy', 'com.comphenix.net.bytebuddy'
archiveFileName = 'ProtocolLib.jar'
}
test {
useJUnitPlatform()
testLogging {
exceptionFormat = 'full'
}
}
compileTestJava {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
processResources {
def includeBuild = isSnapshot && System.getenv('BUILD_NUMBER')
def fullVersion = includeBuild
? version + '-' + System.getenv('BUILD_NUMBER')
: version
eachFile { expand(['version': fullVersion]) }
}
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
afterEvaluate {
artifactId = 'ProtocolLib'
}
}
}
repositories {
maven {
url isSnapshot
? 'https://repo.dmulloy2.net/repository/snapshots/'
: 'https://repo.dmulloy2.net/repository/releases/'
credentials {
username project.nexusUsername
password project.nexusPassword
}
}
}
}
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
tasks.withType(Javadoc) {
options.encoding = 'UTF-8'
}
+174
View File
@@ -0,0 +1,174 @@
//import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
//import io.github.patrick.gradle.remapper.RemapTask
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.shadowJar
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
plugins {
`java-library`
`maven-publish`
`signing`
id("com.gradleup.shadow") version "9.0.2"
// id("io.github.patrick.remapper") version "1.4.2"
id("com.vanniktech.maven.publish") version "0.34.0"
id("io.papermc.paperweight.userdev") version "2.0.0-beta.18"
}
group = "net.dmulloy2"
description = "Provides access to the Minecraft protocol"
val mcVersion = "1.21.8"
val isSnapshot = version.toString().endsWith("-SNAPSHOT")
val commitHash = System.getenv("COMMIT_SHA") ?: ""
val isCI = commitHash.isNotEmpty()
paperweight {
reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArtifactConfiguration.REOBF_PRODUCTION
}
tasks.assemble {
dependsOn(tasks.reobfJar)
}
/*tasks.shadowJar {
dependsOn(tasks.assemble)
}*/
repositories {
if (!isCI) {
mavenLocal()
}
mavenCentral()
maven {
url = uri("https://repo.codemc.io/repository/nms/")
}
maven {
url = uri("https://hub.spigotmc.org/nexus/content/groups/public/")
}
maven {
url = uri("https://libraries.minecraft.net/")
metadataSources {
mavenPom()
artifact()
ignoreGradleMetadataRedirection()
}
}
}
dependencies {
implementation("net.bytebuddy:byte-buddy:1.17.5")
// compileOnly("org.spigotmc:spigot-api:${mcVersion}-R0.1-SNAPSHOT")
// compileOnly("org.spigotmc:spigot:${mcVersion}-R0.1-SNAPSHOT:remapped-mojang")
compileOnly("io.netty:netty-all:4.0.23.Final")
compileOnly("net.kyori:adventure-text-serializer-gson:4.24.0")
compileOnly("net.kyori:adventure-text-serializer-ansi:4.24.0")
compileOnly("com.googlecode.json-simple:json-simple:1.1.1")
paperweight.paperDevBundle("1.21.8-R0.1-SNAPSHOT")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.0")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.10.0")
testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.10.0")
testImplementation("org.mockito:mockito-core:5.6.0")
testImplementation("io.netty:netty-common:4.1.97.Final")
testImplementation("io.netty:netty-transport:4.1.97.Final")
// testImplementation("org.spigotmc:spigot:${mcVersion}-R0.1-SNAPSHOT:remapped-mojang")
testImplementation("net.kyori:adventure-text-serializer-gson:4.24.0")
testImplementation("net.kyori:adventure-text-serializer-plain:4.24.0")
testImplementation("net.kyori:adventure-text-serializer-ansi:4.24.0")
}
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
tasks {
processResources {
val fullVersion = if (isSnapshot && isCI) "${version}-${commitHash.take(7)}" else version
eachFile {
expand("version" to fullVersion)
}
}
test {
useJUnitPlatform()
testLogging {
exceptionFormat = TestExceptionFormat.FULL
}
}
shadowJar {
dependencies {
include(dependency("net.bytebuddy:byte-buddy:.*"))
}
relocate("net.bytebuddy", "com.comphenix.net.bytebuddy")
/*manifest {
attributes(
"paperweight-mappings-namespace" to "mojang"
)
}*/
// archiveFileName = "ProtocolLib.jar"
}
reobfJar {
outputJar = layout.buildDirectory.file("libs/ProtocolLib.jar")
}
javadoc {
options.encoding = "UTF-8"
}
compileJava {
options.encoding = "UTF-8"
}
}
mavenPublishing {
publishToMavenCentral()
signAllPublications()
coordinates("$group", project.name, "$version")
pom {
name.set(project.name)
description.set(project.description)
inceptionYear.set("2012")
url.set("https://github.com/dmulloy2/ProtocolLib")
developers {
developer {
id.set("dmulloy2")
name.set("Dan Mulloy")
url.set("https://dmulloy2.net/")
email.set("dev@dmulloy2.net")
}
}
licenses {
license {
name.set("GNU GENERAL PUBLIC LICENSE - Version 2, June 1991")
url.set("https://www.gnu.org/licenses/gpl-2.0.txt")
distribution.set("repo")
}
}
scm {
tag.set("HEAD")
url.set("https://github.com/dmulloy2/ProtocolLib")
connection.set("scm:git:git://github.com/dmulloy2/ProtocolLib.git")
developerConnection.set("scm:git:git@github.com:dmulloy2/ProtocolLib.git")
}
issueManagement {
system.set("GitHub Issues")
url.set("https://github.com/dmulloy2/ProtocolLib/issues")
}
}
}
+6 -2
View File
@@ -1,2 +1,6 @@
nexusUsername=""
nexusPassword=""
version=5.4.1-SNAPSHOT
mavenCentralUsername=
mavenCentralPassword=
signing.keyId=
signing.password=
signing.secretKeyRingFile=
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
@@ -0,0 +1,38 @@
package com.comphenix.protocol;
import com.comphenix.protocol.injector.PacketFilterManager;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.server.PluginDisableEvent;
class BukkitListener implements Listener {
private final PacketFilterManager manager;
BukkitListener(PacketFilterManager manager) {
this.manager = manager;
}
// @EventHandler(priority = EventPriority.LOWEST)
// public void handleLogin(PlayerLoginEvent event) {
// networkManagerInjector.getInjector(event.getPlayer()).inject();
// }
@EventHandler(priority = EventPriority.LOWEST)
public void handleJoin(PlayerJoinEvent event) {
manager.injectPlayer(event.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR)
public void handleQuit(PlayerQuitEvent event) {
manager.removePlayer(event.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR)
public void handlePluginUnload(PluginDisableEvent event) {
manager.removePacketListeners(event.getPlugin());
}
}
@@ -21,21 +21,19 @@ import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
/**
* Base class for all our commands.
*
* @author Kristian
*/
abstract class CommandBase implements CommandExecutor {
abstract class CommandBase {
public static final ReportType REPORT_COMMAND_ERROR = new ReportType("Cannot execute command %s.");
public static final ReportType REPORT_UNEXPECTED_COMMAND = new ReportType("Incorrect command assigned to %s.");
@@ -58,15 +56,8 @@ abstract class CommandBase implements CommandExecutor {
this.minimumArgumentCount = minimumArgumentCount;
}
@Override
public final boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
public boolean onCommand(CommandSender sender, String[] args) {
try {
// Make sure we're dealing with the correct command
if (!command.getName().equalsIgnoreCase(name)) {
reporter.reportWarning(this, Report.newBuilder(REPORT_UNEXPECTED_COMMAND).messageParam(this));
return false;
}
if (permission != null && !sender.hasPermission(permission)) {
sender.sendMessage(ChatColor.RED + "You haven't got permission to run this command.");
return true;
@@ -81,7 +72,7 @@ abstract class CommandBase implements CommandExecutor {
}
} catch (Throwable ex) {
reporter.reportDetailed(this,
Report.newBuilder(REPORT_COMMAND_ERROR).error(ex).messageParam(name).callerParam(sender, label, args)
Report.newBuilder(REPORT_COMMAND_ERROR).error(ex).messageParam(name).callerParam(sender, name, args)
);
return true;
}
@@ -0,0 +1,22 @@
package com.comphenix.protocol;
import com.comphenix.protocol.injector.PacketFilterManager;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLoginEvent;
class LegacyLoginListener implements Listener {
private final PacketFilterManager manager;
LegacyLoginListener(PacketFilterManager manager) {
this.manager = manager;
}
@SuppressWarnings("deprecation")
@EventHandler(priority = EventPriority.LOWEST)
public void handleLogin(PlayerLoginEvent event) {
manager.injectPlayer(event.getPlayer());
}
}
@@ -16,22 +16,6 @@
*/
package com.comphenix.protocol;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
@@ -48,11 +32,28 @@ import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.PacketFilterManager;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin;
/**
* Logs packets to a given stream
* @author dmulloy2
*/
public class PacketLogging implements CommandExecutor, PacketListener {
public class PacketLogging extends CommandBase implements PacketListener {
public static final String NAME = "packetlog";
private static MethodAccessor HEX_DUMP;
@@ -69,93 +70,94 @@ public class PacketLogging implements CommandExecutor, PacketListener {
private final ProtocolManager manager;
private final Plugin plugin;
PacketLogging(Plugin plugin, ProtocolManager manager) {
PacketLogging(ErrorReporter reporter, Plugin plugin, PacketFilterManager manager) {
super(reporter, CommandBase.PERMISSION_ADMIN, NAME, 2);
this.plugin = plugin;
this.manager = manager;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
public boolean handleCommand(CommandSender sender, String[] args) {
PacketType type = null;
try {
if (args.length > 2) {
Protocol protocol;
if (args.length <= 2) {
sender.sendMessage(ChatColor.RED + "Invalid syntax: /packetlog <protocol> <sender> <packet> [location]");
return true;
}
try {
protocol = Protocol.valueOf(args[0].toUpperCase());
} catch (IllegalArgumentException ex) {
sender.sendMessage(ChatColor.RED + "Unknown protocol " + args[0]);
return true;
}
Protocol protocol;
Sender pSender;
try {
protocol = Protocol.valueOf(args[0].toUpperCase());
} catch (IllegalArgumentException ex) {
sender.sendMessage(ChatColor.RED + "Unknown protocol " + args[0]);
return true;
}
try {
pSender = Sender.valueOf(args[1].toUpperCase());
} catch (IllegalArgumentException ex) {
sender.sendMessage(ChatColor.RED + "Unknown sender: " + args[1]);
return true;
}
Sender pSender;
try {
try { // Try IDs first
int id = Integer.parseInt(args[2]);
type = PacketType.findCurrent(protocol, pSender, id);
} catch (NumberFormatException ex) { // Check packet names
String name = args[2];
for (PacketType packet : PacketType.values()) {
if (packet.getProtocol() == protocol && packet.getSender() == pSender) {
if (packet.name().equalsIgnoreCase(name)) {
try {
pSender = Sender.valueOf(args[1].toUpperCase());
} catch (IllegalArgumentException ex) {
sender.sendMessage(ChatColor.RED + "Unknown sender: " + args[1]);
return true;
}
try {
try { // Try IDs first
int id = Integer.parseInt(args[2]);
type = PacketType.findCurrent(protocol, pSender, id);
} catch (NumberFormatException ex) { // Check packet names
String name = args[2];
for (PacketType packet : PacketType.values()) {
if (packet.getProtocol() == protocol && packet.getSender() == pSender) {
if (packet.name().equalsIgnoreCase(name)) {
type = packet;
break;
}
for (String className : packet.getClassNames()) {
if (className.equalsIgnoreCase(name)) {
type = packet;
break;
}
for (String className : packet.getClassNames()) {
if (className.equalsIgnoreCase(name)) {
type = packet;
break;
}
}
}
}
}
} catch (IllegalArgumentException ex) { // RIP
type = null;
}
} catch (IllegalArgumentException ex) { // RIP
type = null;
}
if (type == null) {
sender.sendMessage(ChatColor.RED + "Unknown packet: " + args[2]);
return true;
}
if (args.length > 3) {
if (args[3].equalsIgnoreCase("console")) {
this.location = LogLocation.CONSOLE;
} else {
this.location = LogLocation.FILE;
}
}
if (pSender == Sender.CLIENT) {
if (receivingTypes.contains(type)) {
receivingTypes.remove(type);
} else {
receivingTypes.add(type);
}
} else {
if (sendingTypes.contains(type)) {
sendingTypes.remove(type);
} else {
sendingTypes.add(type);
}
}
startLogging();
sender.sendMessage(ChatColor.GREEN + "Now logging " + type.getPacketClass().getSimpleName());
if (type == null) {
sender.sendMessage(ChatColor.RED + "Unknown packet: " + args[2]);
return true;
}
sender.sendMessage(ChatColor.RED + "Invalid syntax: /packetlog <protocol> <sender> <packet> [location]");
if (args.length > 3) {
if (args[3].equalsIgnoreCase("console")) {
this.location = LogLocation.CONSOLE;
} else {
this.location = LogLocation.FILE;
}
}
if (pSender == Sender.CLIENT) {
if (receivingTypes.contains(type)) {
receivingTypes.remove(type);
} else {
receivingTypes.add(type);
}
} else {
if (sendingTypes.contains(type)) {
sendingTypes.remove(type);
} else {
sendingTypes.add(type);
}
}
startLogging();
sender.sendMessage(ChatColor.GREEN + "Now logging " + type.getPacketClass().getSimpleName());
return true;
} catch (Throwable ex) {
sender.sendMessage(ChatColor.RED + "Failed to parse command: " + ex);
@@ -14,18 +14,18 @@ import java.util.Map;
import java.util.UUID;
import java.util.function.BiConsumer;
import com.comphenix.protocol.utility.WordUtils;
import org.bukkit.Bukkit;
import com.comphenix.protocol.PacketTypeLookup.ClassLookup;
import com.comphenix.protocol.events.ConnectionSide;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.scheduler.UniversalRunnable;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.base.Preconditions;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Iterables;
import org.apache.commons.lang.WordUtils;
import org.bukkit.Bukkit;
/**
* Represents the type of a packet in a specific protocol.
* <p>
@@ -109,36 +109,36 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType BUNDLE = new PacketType(PROTOCOL, SENDER, 0x00, "BundleDelimiter", "Delimiter", "BundleDelimiterPacket");
public static final PacketType SPAWN_ENTITY = new PacketType(PROTOCOL, SENDER, 0x01, "AddEntity", "SpawnEntity", "SPacketSpawnObject");
public static final PacketType SPAWN_ENTITY_EXPERIENCE_ORB = new PacketType(PROTOCOL, SENDER, 0x02, "AddExperienceOrb", "SpawnEntityExperienceOrb", "SPacketSpawnExperienceOrb");
public static final PacketType ANIMATION = new PacketType(PROTOCOL, SENDER, 0x03, "Animate", "Animation", "SPacketAnimation");
public static final PacketType STATISTIC = new PacketType(PROTOCOL, SENDER, 0x04, "AwardStats", "Statistic", "SPacketStatistics");
public static final PacketType BLOCK_CHANGED_ACK = new PacketType(PROTOCOL, SENDER, 0x05, "BlockChangedAck");
public static final PacketType BLOCK_BREAK_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x06, "BlockDestruction", "BlockBreakAnimation", "SPacketBlockBreakAnim");
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x07, "BlockEntityData", "TileEntityData", "SPacketUpdateTileEntity");
public static final PacketType BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x08, "BlockEvent", "BlockAction", "SPacketBlockAction");
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x09, "BlockUpdate", "BlockChange", "SPacketBlockChange");
public static final PacketType BOSS = new PacketType(PROTOCOL, SENDER, 0x0A, "BossEvent", "Boss", "SPacketUpdateBossInfo");
public static final PacketType SERVER_DIFFICULTY = new PacketType(PROTOCOL, SENDER, 0x0B, "ChangeDifficulty", "ServerDifficulty", "SPacketServerDifficulty");
public static final PacketType CHUNK_BATCH_FINISHED = new PacketType(PROTOCOL, SENDER, 0x0C, "ChunkBatchFinished");
public static final PacketType CHUNK_BATCH_START = new PacketType(PROTOCOL, SENDER, 0x0D, "ChunkBatchStart");
public static final PacketType CHUNKS_BIOMES = new PacketType(PROTOCOL, SENDER, 0x0E, "ChunksBiomes", "ClientboundChunksBiomesPacket");
public static final PacketType CLEAR_TITLES = new PacketType(PROTOCOL, SENDER, 0x0F, "ClearTitles");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x10, "CommandSuggestions", "TabComplete", "SPacketTabComplete");
public static final PacketType COMMANDS = new PacketType(PROTOCOL, SENDER, 0x11, "Commands");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x12, "ContainerClose", "CloseWindow", "SPacketCloseWindow");
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x13, "ContainerSetContent", "WindowItems", "SPacketWindowItems");
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x14, "ContainerSetData", "WindowData", "SPacketWindowProperty");
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x15, "ContainerSetSlot", "SetSlot", "SPacketSetSlot");
public static final PacketType COOKIE_REQUEST = new PacketType(PROTOCOL, SENDER, 0x16, "CookieRequest");
public static final PacketType SET_COOLDOWN = new PacketType(PROTOCOL, SENDER, 0x17, "Cooldown", "SetCooldown", "SPacketCooldown");
public static final PacketType CUSTOM_CHAT_COMPLETIONS = new PacketType(PROTOCOL, SENDER, 0x18, "CustomChatCompletions");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x19, "CustomPayload", "SPacketCustomPayload");
public static final PacketType DAMAGE_EVENT = new PacketType(PROTOCOL, SENDER, 0x1A, "DamageEvent", "ClientboundDamageEventPacket");
public static final PacketType DEBUG_SAMPLE = new PacketType(PROTOCOL, SENDER, 0x1B, "DebugSample");
public static final PacketType DELETE_CHAT_MESSAGE = new PacketType(PROTOCOL, SENDER, 0x1C, "DeleteChat");
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x1D, "Disconnect", "KickDisconnect", "SPacketDisconnect");
public static final PacketType DISGUISED_CHAT = new PacketType(PROTOCOL, SENDER, 0x1E, "DisguisedChat");
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x1F, "EntityEvent", "EntityStatus", "SPacketEntityStatus");
public static final PacketType ANIMATION = new PacketType(PROTOCOL, SENDER, 0x02, "Animate", "Animation", "SPacketAnimation");
public static final PacketType STATISTIC = new PacketType(PROTOCOL, SENDER, 0x03, "AwardStats", "Statistic", "SPacketStatistics");
public static final PacketType BLOCK_CHANGED_ACK = new PacketType(PROTOCOL, SENDER, 0x04, "BlockChangedAck");
public static final PacketType BLOCK_BREAK_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x05, "BlockDestruction", "BlockBreakAnimation", "SPacketBlockBreakAnim");
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x06, "BlockEntityData", "TileEntityData", "SPacketUpdateTileEntity");
public static final PacketType BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x07, "BlockEvent", "BlockAction", "SPacketBlockAction");
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x08, "BlockUpdate", "BlockChange", "SPacketBlockChange");
public static final PacketType BOSS = new PacketType(PROTOCOL, SENDER, 0x09, "BossEvent", "Boss", "SPacketUpdateBossInfo");
public static final PacketType SERVER_DIFFICULTY = new PacketType(PROTOCOL, SENDER, 0x0A, "ChangeDifficulty", "ServerDifficulty", "SPacketServerDifficulty");
public static final PacketType CHUNK_BATCH_FINISHED = new PacketType(PROTOCOL, SENDER, 0x0B, "ChunkBatchFinished");
public static final PacketType CHUNK_BATCH_START = new PacketType(PROTOCOL, SENDER, 0x0C, "ChunkBatchStart");
public static final PacketType CHUNKS_BIOMES = new PacketType(PROTOCOL, SENDER, 0x0D, "ChunksBiomes", "ClientboundChunksBiomesPacket");
public static final PacketType CLEAR_TITLES = new PacketType(PROTOCOL, SENDER, 0x0E, "ClearTitles");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x0F, "CommandSuggestions", "TabComplete", "SPacketTabComplete");
public static final PacketType COMMANDS = new PacketType(PROTOCOL, SENDER, 0x10, "Commands");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x11, "ContainerClose", "CloseWindow", "SPacketCloseWindow");
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x12, "ContainerSetContent", "WindowItems", "SPacketWindowItems");
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x13, "ContainerSetData", "WindowData", "SPacketWindowProperty");
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x14, "ContainerSetSlot", "SetSlot", "SPacketSetSlot");
public static final PacketType COOKIE_REQUEST = new PacketType(PROTOCOL, SENDER, 0x15, "CookieRequest");
public static final PacketType SET_COOLDOWN = new PacketType(PROTOCOL, SENDER, 0x16, "Cooldown", "SetCooldown", "SPacketCooldown");
public static final PacketType CUSTOM_CHAT_COMPLETIONS = new PacketType(PROTOCOL, SENDER, 0x17, "CustomChatCompletions");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x18, "CustomPayload", "SPacketCustomPayload");
public static final PacketType DAMAGE_EVENT = new PacketType(PROTOCOL, SENDER, 0x19, "DamageEvent", "ClientboundDamageEventPacket");
public static final PacketType DEBUG_SAMPLE = new PacketType(PROTOCOL, SENDER, 0x1A, "DebugSample");
public static final PacketType DELETE_CHAT_MESSAGE = new PacketType(PROTOCOL, SENDER, 0x1B, "DeleteChat");
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x1C, "Disconnect", "KickDisconnect", "SPacketDisconnect");
public static final PacketType DISGUISED_CHAT = new PacketType(PROTOCOL, SENDER, 0x1D, "DisguisedChat");
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x1E, "EntityEvent", "EntityStatus", "SPacketEntityStatus");
public static final PacketType ENTITY_POSITION_SYNC = new PacketType(PROTOCOL, SENDER, 0x1F, "EntityPositionSync");
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x20, "Explode", "Explosion", "SPacketExplosion");
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x21, "ForgetLevelChunk", "UnloadChunk", "SPacketUnloadChunk");
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x22, "GameEvent", "GameStateChange", "SPacketChangeGameState");
@@ -155,84 +155,92 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType OPEN_WINDOW_MERCHANT = new PacketType(PROTOCOL, SENDER, 0x2D, "MerchantOffers", "OpenWindowMerchant");
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x2E, "net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Pos", "Entity$RelEntityMove", "Entity$PacketPlayOutRelEntityMove");
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x2F, "net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$PosRot", "Entity$RelEntityMoveLook", "Entity$PacketPlayOutRelEntityMoveLook");
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x30, "net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Rot", "Entity$EntityLook", "Entity$PacketPlayOutEntityLook");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x31, "MoveVehicle", "VehicleMove", "SPacketMoveVehicle");
public static final PacketType OPEN_BOOK = new PacketType(PROTOCOL, SENDER, 0x32, "OpenBook");
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x33, "OpenScreen", "OpenWindow", "SPacketOpenWindow");
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x34, "OpenSignEditor", "SPacketSignEditorOpen");
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x35, "Ping");
public static final PacketType PONG_RESPONSE = new PacketType(PROTOCOL, SENDER, 0x36, "PongResponse");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x37, "PlaceGhostRecipe", "AutoRecipe", "SPacketPlaceGhostRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x38, "PlayerAbilities", "Abilities", "SPacketPlayerAbilities");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x39, "PlayerChat", "Chat", "SPacketChat");
public static final PacketType PLAYER_COMBAT_END = new PacketType(PROTOCOL, SENDER, 0x3A, "PlayerCombatEnd");
public static final PacketType PLAYER_COMBAT_ENTER = new PacketType(PROTOCOL, SENDER, 0x3B, "PlayerCombatEnter");
public static final PacketType PLAYER_COMBAT_KILL = new PacketType(PROTOCOL, SENDER, 0x3C, "PlayerCombatKill");
public static final PacketType PLAYER_INFO_REMOVE = new PacketType(PROTOCOL, SENDER, 0x3D, "PlayerInfoRemove");
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x3E, "PlayerInfoUpdate", "PlayerInfo");
public static final PacketType LOOK_AT = new PacketType(PROTOCOL, SENDER, 0x3F, "PlayerLookAt", "LookAt", "SPacketPlayerPosLook");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x40, "PlayerPosition", "Position");
public static final PacketType RECIPES = new PacketType(PROTOCOL, SENDER, 0x41, "Recipe", "Recipes", "SPacketRecipeBook");
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x42, "RemoveEntities", "EntityDestroy", "SPacketDestroyEntities");
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x43, "RemoveMobEffect", "RemoveEntityEffect", "SPacketRemoveEntityEffect");
public static final PacketType RESET_SCORE = new PacketType(PROTOCOL, SENDER, 0x44, "ResetScore", "ResetScorePacket");
public static final PacketType REMOVE_RESOURCE_PACK = new PacketType(PROTOCOL, SENDER, 0x45, "ResourcePackPop", "ResourcePackPopPacket");
public static final PacketType ADD_RESOURCE_PACK = new PacketType(PROTOCOL, SENDER, 0x46, "ResourcePackPush", "ResourcePackPushPacket");
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x47, "Respawn", "SPacketRespawn");
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x48, "RotateHead", "EntityHeadRotation", "SPacketEntityHeadLook");
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x49, "SectionBlocksUpdate", "MultiBlockChange", "SPacketMultiBlockChange");
public static final PacketType SELECT_ADVANCEMENT_TAB = new PacketType(PROTOCOL, SENDER, 0x4A, "SelectAdvancementsTab", "SelectAdvancementTab", "SPacketSelectAdvancementsTab");
public static final PacketType SERVER_DATA = new PacketType(PROTOCOL, SENDER, 0x4B, "ServerData");
public static final PacketType SET_ACTION_BAR_TEXT = new PacketType(PROTOCOL, SENDER, 0x4C, "SetActionBarText");
public static final PacketType SET_BORDER_CENTER = new PacketType(PROTOCOL, SENDER, 0x4D, "SetBorderCenter");
public static final PacketType SET_BORDER_LERP_SIZE = new PacketType(PROTOCOL, SENDER, 0x4E, "SetBorderLerpSize");
public static final PacketType SET_BORDER_SIZE = new PacketType(PROTOCOL, SENDER, 0x4F, "SetBorderSize");
public static final PacketType SET_BORDER_WARNING_DELAY = new PacketType(PROTOCOL, SENDER, 0x50, "SetBorderWarningDelay");
public static final PacketType SET_BORDER_WARNING_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x51, "SetBorderWarningDistance");
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x52, "SetCamera", "Camera", "SPacketCamera");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x53, "SetCarriedItem", "HeldItemSlot", "SPacketHeldItemChange");
public static final PacketType VIEW_CENTRE = new PacketType(PROTOCOL, SENDER, 0x54, "SetChunkCacheCenter", "ViewCentre");
public static final PacketType VIEW_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x55, "SetChunkCacheRadius", "ViewDistance");
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x56, "SetDefaultSpawnPosition", "SpawnPosition", "SPacketSpawnPosition");
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x57, "SetDisplayObjective", "ScoreboardDisplayObjective", "SPacketDisplayObjective");
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x58, "SetEntityData", "EntityMetadata", "SPacketEntityMetadata");
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x59, "SetEntityLink", "AttachEntity", "SPacketEntityAttach");
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x5A, "SetEntityMotion", "EntityVelocity", "SPacketEntityVelocity");
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x5B, "SetEquipment", "EntityEquipment", "SPacketEntityEquipment");
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x5C, "SetExperience", "Experience", "SPacketSetExperience");
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x5D, "SetHealth", "UpdateHealth", "SPacketUpdateHealth");
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x5E, "SetObjective", "ScoreboardObjective", "SPacketScoreboardObjective");
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x5F, "SetPassengers", "Mount", "SPacketSetPassengers");
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x60, "SetPlayerTeam", "ScoreboardTeam", "SPacketTeams");
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x61, "SetScore", "ScoreboardScore", "SPacketUpdateScore");
public static final PacketType UPDATE_SIMULATION_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x62, "SetSimulationDistance");
public static final PacketType SET_SUBTITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x63, "SetSubtitleText");
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x64, "SetTime", "UpdateTime", "SPacketTimeUpdate");
public static final PacketType SET_TITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x65, "SetTitleText");
public static final PacketType SET_TITLES_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x66, "SetTitlesAnimation");
public static final PacketType ENTITY_SOUND = new PacketType(PROTOCOL, SENDER, 0x67, "SoundEntity", "EntitySound", "SPacketSoundEffect");
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x68, "Sound", "NamedSoundEffect");
public static final PacketType START_CONFIGURATION = new PacketType(PROTOCOL, SENDER, 0x69, "StartConfiguration");
public static final PacketType STOP_SOUND = new PacketType(PROTOCOL, SENDER, 0x6A, "StopSound");
public static final PacketType STORE_COOKIE = new PacketType(PROTOCOL, SENDER, 0x6B, "StoreCookie");
public static final PacketType SYSTEM_CHAT = new PacketType(PROTOCOL, SENDER, 0x6C, "SystemChat");
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x6D, "TabList", "PlayerListHeaderFooter", "SPacketPlayerListHeaderFooter");
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x6E, "TagQuery", "NBTQuery");
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x6F, "TakeItemEntity", "Collect", "SPacketCollectItem");
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x70, "TeleportEntity", "EntityTeleport", "SPacketEntityTeleport");
public static final PacketType TICKING_STATE = new PacketType(PROTOCOL, SENDER, 0x71, "TickingState", "TickingStatePacket");
public static final PacketType TICKING_STEP_STATE = new PacketType(PROTOCOL, SENDER, 0x72, "TickingStep", "TickingStepPacket");
public static final PacketType TRANSFER = new PacketType(PROTOCOL, SENDER, 0x73, "Transfer");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x74, "UpdateAdvancements", "Advancements", "SPacketAdvancementInfo");
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x75, "UpdateAttributes", "SPacketEntityProperties");
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x76, "UpdateMobEffect", "EntityEffect", "SPacketEntityEffect");
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x77, "UpdateRecipes", "RecipeUpdate");
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x78, "UpdateTags", "Tags");
public static final PacketType PROJECTILE_POWER = new PacketType(PROTOCOL, SENDER, 0x79, "ProjectilePower");
public static final PacketType REPORT_DETAILS = new PacketType(PROTOCOL, SENDER, 0x7A, "CustomReportDetails");
public static final PacketType SERVER_LINKS = new PacketType(PROTOCOL, SENDER, 0x7B, "ServerLinks");
// ---- Removed in 1.9
public static final PacketType MOVE_MINECART = new PacketType(PROTOCOL, SENDER, 0x30, "MoveMinecart");
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x31, "net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Rot", "Entity$EntityLook", "Entity$PacketPlayOutEntityLook");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x32, "MoveVehicle", "VehicleMove", "SPacketMoveVehicle");
public static final PacketType OPEN_BOOK = new PacketType(PROTOCOL, SENDER, 0x33, "OpenBook");
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x34, "OpenScreen", "OpenWindow", "SPacketOpenWindow");
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x35, "OpenSignEditor", "SPacketSignEditorOpen");
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x36, "Ping");
public static final PacketType PONG_RESPONSE = new PacketType(PROTOCOL, SENDER, 0x37, "PongResponse");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x38, "PlaceGhostRecipe", "AutoRecipe", "SPacketPlaceGhostRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x39, "PlayerAbilities", "Abilities", "SPacketPlayerAbilities");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x3A, "PlayerChat", "Chat", "SPacketChat");
public static final PacketType PLAYER_COMBAT_END = new PacketType(PROTOCOL, SENDER, 0x3B, "PlayerCombatEnd");
public static final PacketType PLAYER_COMBAT_ENTER = new PacketType(PROTOCOL, SENDER, 0x3C, "PlayerCombatEnter");
public static final PacketType PLAYER_COMBAT_KILL = new PacketType(PROTOCOL, SENDER, 0x3D, "PlayerCombatKill");
public static final PacketType PLAYER_INFO_REMOVE = new PacketType(PROTOCOL, SENDER, 0x3E, "PlayerInfoRemove");
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x3F, "PlayerInfoUpdate", "PlayerInfo");
public static final PacketType LOOK_AT = new PacketType(PROTOCOL, SENDER, 0x40, "PlayerLookAt", "LookAt", "SPacketPlayerPosLook");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x41, "PlayerPosition", "Position");
public static final PacketType PLAYER_ROTATION = new PacketType(PROTOCOL, SENDER, 0x42, "PlayerRotation");
public static final PacketType RECIPE_BOOK_ADD = new PacketType(PROTOCOL, SENDER, 0x43, "RecipeBookAdd");
public static final PacketType RECIPE_BOOK_REMOVE = new PacketType(PROTOCOL, SENDER, 0x44, "RecipeBookRemove");
public static final PacketType RECIPE_BOOK_SETTINGS = new PacketType(PROTOCOL, SENDER, 0x45, "RecipeBookSettings");
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x46, "RemoveEntities", "EntityDestroy", "SPacketDestroyEntities");
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x47, "RemoveMobEffect", "RemoveEntityEffect", "SPacketRemoveEntityEffect");
public static final PacketType RESET_SCORE = new PacketType(PROTOCOL, SENDER, 0x48, "ResetScore", "ResetScorePacket");
public static final PacketType REMOVE_RESOURCE_PACK = new PacketType(PROTOCOL, SENDER, 0x49, "ResourcePackPop", "ResourcePackPopPacket");
public static final PacketType ADD_RESOURCE_PACK = new PacketType(PROTOCOL, SENDER, 0x4A, "ResourcePackPush", "ResourcePackPushPacket");
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x4B, "Respawn", "SPacketRespawn");
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x4C, "RotateHead", "EntityHeadRotation", "SPacketEntityHeadLook");
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x4D, "SectionBlocksUpdate", "MultiBlockChange", "SPacketMultiBlockChange");
public static final PacketType SELECT_ADVANCEMENT_TAB = new PacketType(PROTOCOL, SENDER, 0x4E, "SelectAdvancementsTab", "SelectAdvancementTab", "SPacketSelectAdvancementsTab");
public static final PacketType SERVER_DATA = new PacketType(PROTOCOL, SENDER, 0x4F, "ServerData");
public static final PacketType SET_ACTION_BAR_TEXT = new PacketType(PROTOCOL, SENDER, 0x50, "SetActionBarText");
public static final PacketType SET_BORDER_CENTER = new PacketType(PROTOCOL, SENDER, 0x51, "SetBorderCenter");
public static final PacketType SET_BORDER_LERP_SIZE = new PacketType(PROTOCOL, SENDER, 0x52, "SetBorderLerpSize");
public static final PacketType SET_BORDER_SIZE = new PacketType(PROTOCOL, SENDER, 0x53, "SetBorderSize");
public static final PacketType SET_BORDER_WARNING_DELAY = new PacketType(PROTOCOL, SENDER, 0x54, "SetBorderWarningDelay");
public static final PacketType SET_BORDER_WARNING_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x55, "SetBorderWarningDistance");
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x56, "SetCamera", "Camera", "SPacketCamera");
public static final PacketType VIEW_CENTRE = new PacketType(PROTOCOL, SENDER, 0x57, "SetChunkCacheCenter", "ViewCentre");
public static final PacketType VIEW_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x58, "SetChunkCacheRadius", "ViewDistance");
public static final PacketType SET_CURSOR_ITEM = new PacketType(PROTOCOL, SENDER, 0x59, "SetCursorItem");
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x5A, "SetDefaultSpawnPosition", "SpawnPosition", "SPacketSpawnPosition");
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x5B, "SetDisplayObjective", "ScoreboardDisplayObjective", "SPacketDisplayObjective");
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x5C, "SetEntityData", "EntityMetadata", "SPacketEntityMetadata");
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x5D, "SetEntityLink", "AttachEntity", "SPacketEntityAttach");
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x5E, "SetEntityMotion", "EntityVelocity", "SPacketEntityVelocity");
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x5F, "SetEquipment", "EntityEquipment", "SPacketEntityEquipment");
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x60, "SetExperience", "Experience", "SPacketSetExperience");
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x61, "SetHealth", "UpdateHealth", "SPacketUpdateHealth");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x62, "SetHeldSlot", "SetCarriedItem", "HeldItemSlot", "SPacketHeldItemChange");
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x63, "SetObjective", "ScoreboardObjective", "SPacketScoreboardObjective");
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x64, "SetPassengers", "Mount", "SPacketSetPassengers");
public static final PacketType SET_PLAYER_INVENTORY = new PacketType(PROTOCOL, SENDER, 0x65, "SetPlayerInventory");
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x66, "SetPlayerTeam", "ScoreboardTeam", "SPacketTeams");
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x67, "SetScore", "ScoreboardScore", "SPacketUpdateScore");
public static final PacketType UPDATE_SIMULATION_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x68, "SetSimulationDistance");
public static final PacketType SET_SUBTITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x69, "SetSubtitleText");
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x6A, "SetTime", "UpdateTime", "SPacketTimeUpdate");
public static final PacketType SET_TITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x6B, "SetTitleText");
public static final PacketType SET_TITLES_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x6C, "SetTitlesAnimation");
public static final PacketType ENTITY_SOUND = new PacketType(PROTOCOL, SENDER, 0x6D, "SoundEntity", "EntitySound", "SPacketSoundEffect");
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x6E, "Sound", "NamedSoundEffect");
public static final PacketType START_CONFIGURATION = new PacketType(PROTOCOL, SENDER, 0x6F, "StartConfiguration");
public static final PacketType STOP_SOUND = new PacketType(PROTOCOL, SENDER, 0x70, "StopSound");
public static final PacketType STORE_COOKIE = new PacketType(PROTOCOL, SENDER, 0x71, "StoreCookie");
public static final PacketType SYSTEM_CHAT = new PacketType(PROTOCOL, SENDER, 0x72, "SystemChat");
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x73, "TabList", "PlayerListHeaderFooter", "SPacketPlayerListHeaderFooter");
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x74, "TagQuery", "NBTQuery");
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x75, "TakeItemEntity", "Collect", "SPacketCollectItem");
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x76, "TeleportEntity", "EntityTeleport", "SPacketEntityTeleport");
public static final PacketType TEST_INSTANCE_BLOCK_STATUS = new PacketType(PROTOCOL, SENDER, 0x77, "ClientboundTestInstanceBlockStatus");
public static final PacketType TICKING_STATE = new PacketType(PROTOCOL, SENDER, 0x78, "TickingState", "TickingStatePacket");
public static final PacketType TICKING_STEP_STATE = new PacketType(PROTOCOL, SENDER, 0x79, "TickingStep", "TickingStepPacket");
public static final PacketType TRANSFER = new PacketType(PROTOCOL, SENDER, 0x7A, "Transfer");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x7B, "UpdateAdvancements", "Advancements", "SPacketAdvancementInfo");
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x7C, "UpdateAttributes", "SPacketEntityProperties");
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x7D, "UpdateMobEffect", "EntityEffect", "SPacketEntityEffect");
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x7E, "UpdateRecipes", "RecipeUpdate");
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x7F, "UpdateTags", "Tags");
public static final PacketType PROJECTILE_POWER = new PacketType(PROTOCOL, SENDER, 0x80, "ProjectilePower");
public static final PacketType REPORT_DETAILS = new PacketType(PROTOCOL, SENDER, 0x81, "CustomReportDetails");
public static final PacketType SERVER_LINKS = new PacketType(PROTOCOL, SENDER, 0x82, "ServerLinks");
public static final PacketType TRACKED_WAYPOINT = new PacketType(PROTOCOL, SENDER, 0x83, "TrackedWaypoint");
public static final PacketType CLEAR_DIALOG = new PacketType(PROTOCOL, SENDER, 0x84, "ClearDialog");
public static final PacketType SHOW_DIALOG = new PacketType(PROTOCOL, SENDER, 0x85, "ShowDialog");
/**
* @deprecated Removed in 1.9
@@ -252,8 +260,6 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
@Deprecated
public static final PacketType UPDATE_ENTITY_NBT = new PacketType(PROTOCOL, SENDER, 253, "UpdateEntityNBT");
// ----- Renamed packets
/**
* @deprecated Renamed to {@link #WINDOW_DATA}
*/
@@ -278,8 +284,6 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
@Deprecated
public static final PacketType OPEN_SIGN_ENTITY = OPEN_SIGN_EDITOR.clone();
// ----- Replaced in 1.9.4
/**
* @deprecated Replaced by {@link #TILE_ENTITY_DATA}
*/
@@ -287,8 +291,6 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType UPDATE_SIGN = MinecraftReflection.signUpdateExists() ? new PacketType(PROTOCOL, SENDER, 252, "UpdateSign") :
TILE_ENTITY_DATA.clone();
// ---- Removed in 1.14
/**
* @deprecated Removed in 1.14
*/
@@ -403,6 +405,19 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
@Deprecated
public static final PacketType RESOURCE_PACK_SEND = new PacketType(PROTOCOL, SENDER, 234, "ResourcePackSend", "SPacketResourcePackSend");
/**
* @deprecated Removed in 1.21.2: replaced with individual packets for adding/removing recipes
*/
@Deprecated
public static final PacketType RECIPES = new PacketType(PROTOCOL, SENDER, 233, "Recipe", "Recipes", "SPacketRecipeBook");
/**
* @deprecated Removed in 1.21.5; replace by SPAWN_ENTITY
*/
@Deprecated
public static final PacketType SPAWN_ENTITY_EXPERIENCE_ORB = new PacketType(PROTOCOL, SENDER, 232, "AddExperienceOrb", "SpawnEntityExperienceOrb", "SPacketSpawnExperienceOrb");
private static final Server INSTANCE = new Server();
// Prevent accidental construction
@@ -425,62 +440,70 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType TELEPORT_ACCEPT = new PacketType(PROTOCOL, SENDER, 0x00, "AcceptTeleportation", "TeleportAccept", "CPacketConfirmTeleport");
public static final PacketType TILE_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x01, "BlockEntityTagQuery", "TileNBTQuery");
public static final PacketType DIFFICULTY_CHANGE = new PacketType(PROTOCOL, SENDER, 0x02, "ChangeDifficulty", "DifficultyChange");
public static final PacketType CHAT_ACK = new PacketType(PROTOCOL, SENDER, 0x03, "ChatAck");
public static final PacketType CHAT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x04, "ChatCommand");
public static final PacketType CHAT_COMMAND_SIGNED = new PacketType(PROTOCOL, SENDER, 0x05, "ChatCommandSigned");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x06, "Chat", "CPacketChatMessage");
public static final PacketType CHAT_SESSION_UPDATE = new PacketType(PROTOCOL, SENDER, 0x07, "ChatSessionUpdate");
public static final PacketType CHUNK_BATCH_RECEIVED = new PacketType(PROTOCOL, SENDER, 0x08, "ChunkBatchReceived");
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x09, "ClientCommand", "CPacketClientStatus");
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x0A, "ClientInformation", "Settings", "CPacketClientSettings");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x0B, "CommandSuggestion", "TabComplete", "CPacketTabComplete");
public static final PacketType CONFIGURATION_ACK = new PacketType(PROTOCOL, SENDER, 0x0C, "ConfigurationAcknowledged");
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x0D, "ContainerButtonClick", "EnchantItem", "CPacketEnchantItem");
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x0E, "ContainerClick", "WindowClick", "CPacketClickWindow");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x0F, "ContainerClose", "CloseWindow", "CPacketCloseWindow");
public static final PacketType CONTAINER_SLOT_STATE_CHANGED = new PacketType(PROTOCOL, SENDER, 0x10, "ContainerSlotStateChanged", "ContainerSlotStateChangedPacket");
public static final PacketType COOKIE_RESPONSE = new PacketType(PROTOCOL, SENDER, 0x11, "CookieResponse");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x12, "CustomPayload", "CPacketCustomPayload");
public static final PacketType DEBUG_SAMPLE_SUBSCRIPTION = new PacketType(PROTOCOL, SENDER, 0x13, "DebugSampleSubscription");
public static final PacketType B_EDIT = new PacketType(PROTOCOL, SENDER, 0x14, "EditBook", "BEdit");
public static final PacketType ENTITY_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x15, "EntityTagQuery", "EntityNBTQuery");
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x16, "Interact", "UseEntity", "CPacketUseEntity");
public static final PacketType JIGSAW_GENERATE = new PacketType(PROTOCOL, SENDER, 0x17, "JigsawGenerate");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x18, "KeepAlive", "CPacketKeepAlive");
public static final PacketType DIFFICULTY_LOCK = new PacketType(PROTOCOL, SENDER, 0x19, "LockDifficulty", "DifficultyLock");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x1A, "net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Pos", "Flying$Position", "Flying$PacketPlayInPosition", "CPacketPlayer$Position");
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x1B, "net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$PosRot", "Flying$PositionLook", "Flying$PacketPlayInPositionLook", "CPacketPlayer$PositionRotation");
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x1C, "net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Rot", "Flying$Look", "Flying$PacketPlayInLook", "CPacketPlayer$Rotation");
public static final PacketType GROUND = new PacketType(PROTOCOL, SENDER, 0x1D, "net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$StatusOnly", "Flying$d");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x1E, "MoveVehicle", "VehicleMove", "CPacketVehicleMove");
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x1F, "PaddleBoat", "BoatMove", "CPacketSteerBoat");
public static final PacketType PICK_ITEM = new PacketType(PROTOCOL, SENDER, 0x20, "PickItem");
public static final PacketType PING_REQUEST = new PacketType(PROTOCOL, SENDER, 0x21, "PingRequest");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x22, "PlaceRecipe", "AutoRecipe", "CPacketPlaceRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x23, "PlayerAbilities", "Abilities", "CPacketPlayerAbilities");
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x24, "PlayerAction", "BlockDig", "CPacketPlayerDigging");
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x25, "PlayerCommand", "EntityAction", "CPacketEntityAction");
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x26, "PlayerInput", "SteerVehicle", "CPacketInput");
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x27, "Pong");
public static final PacketType RECIPE_SETTINGS = new PacketType(PROTOCOL, SENDER, 0x28, "RecipeBookChangeSettings", "RecipeSettings");
public static final PacketType RECIPE_DISPLAYED = new PacketType(PROTOCOL, SENDER, 0x29, "RecipeBookSeenRecipe", "RecipeDisplayed", "CPacketRecipeInfo");
public static final PacketType ITEM_NAME = new PacketType(PROTOCOL, SENDER, 0x2A, "RenameItem", "ItemName");
public static final PacketType RESOURCE_PACK_STATUS = new PacketType(PROTOCOL, SENDER, 0x2B, "ResourcePack", "ResourcePackStatus", "CPacketResourcePackStatus");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x2C, "SeenAdvancements", "Advancements", "CPacketSeenAdvancements");
public static final PacketType TR_SEL = new PacketType(PROTOCOL, SENDER, 0x2D, "SelectTrade", "TrSel");
public static final PacketType BEACON = new PacketType(PROTOCOL, SENDER, 0x2E, "SetBeacon", "Beacon");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x2F, "SetCarriedItem", "HeldItemSlot", "CPacketHeldItemChange");
public static final PacketType SET_COMMAND_BLOCK = new PacketType(PROTOCOL, SENDER, 0x30, "SetCommandBlock");
public static final PacketType SET_COMMAND_MINECART = new PacketType(PROTOCOL, SENDER, 0x31, "SetCommandMinecart");
public static final PacketType SET_CREATIVE_SLOT = new PacketType(PROTOCOL, SENDER, 0x32, "SetCreativeModeSlot", "SetCreativeSlot", "CPacketCreativeInventoryAction");
public static final PacketType SET_JIGSAW = new PacketType(PROTOCOL, SENDER, 0x33, "SetJigsawBlock", "SetJigsaw");
public static final PacketType STRUCT = new PacketType(PROTOCOL, SENDER, 0x34, "SetStructureBlock", "Struct");
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x35, "SignUpdate", "UpdateSign", "CPacketUpdateSign");
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x36, "Swing", "ArmAnimation", "CPacketAnimation");
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x37, "TeleportToEntity", "Spectate", "CPacketSpectate");
public static final PacketType USE_ITEM_ON = new PacketType(PROTOCOL, SENDER, 0x38, "UseItemOn", "BlockPlace", "CPacketPlayerTryUseItemOnBlock");
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x39, "UseItem", "CPacketPlayerTryUseItem");
public static final PacketType SELECT_BUNDLE_ITEM = new PacketType(PROTOCOL, SENDER, 0x02, "SelectBundleItem");
public static final PacketType DIFFICULTY_CHANGE = new PacketType(PROTOCOL, SENDER, 0x03, "ChangeDifficulty", "DifficultyChange");
public static final PacketType CHANGE_GAME_MODE = new PacketType(PROTOCOL, SENDER, 0x04, "ChangeGameMode");
public static final PacketType CHAT_ACK = new PacketType(PROTOCOL, SENDER, 0x05, "ChatAck");
public static final PacketType CHAT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x06, "ChatCommand");
public static final PacketType CHAT_COMMAND_SIGNED = new PacketType(PROTOCOL, SENDER, 0x07, "ChatCommandSigned");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x08, "Chat", "CPacketChatMessage");
public static final PacketType CHAT_SESSION_UPDATE = new PacketType(PROTOCOL, SENDER, 0x09, "ChatSessionUpdate");
public static final PacketType CHUNK_BATCH_RECEIVED = new PacketType(PROTOCOL, SENDER, 0x0A, "ChunkBatchReceived");
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x0B, "ClientCommand", "CPacketClientStatus");
public static final PacketType CLIENT_TICK_END = new PacketType(PROTOCOL, SENDER, 0x0C, "ClientTickEnd");
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x0D, "ClientInformation", "Settings", "CPacketClientSettings");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x0E, "CommandSuggestion", "TabComplete", "CPacketTabComplete");
public static final PacketType CONFIGURATION_ACK = new PacketType(PROTOCOL, SENDER, 0x0F, "ConfigurationAcknowledged");
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x10, "ContainerButtonClick", "EnchantItem", "CPacketEnchantItem");
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x11, "ContainerClick", "WindowClick", "CPacketClickWindow");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x12, "ContainerClose", "CloseWindow", "CPacketCloseWindow");
public static final PacketType CONTAINER_SLOT_STATE_CHANGED = new PacketType(PROTOCOL, SENDER, 0x13, "ContainerSlotStateChanged", "ContainerSlotStateChangedPacket");
public static final PacketType COOKIE_RESPONSE = new PacketType(PROTOCOL, SENDER, 0x14, "CookieResponse");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x15, "CustomPayload", "CPacketCustomPayload");
public static final PacketType DEBUG_SAMPLE_SUBSCRIPTION = new PacketType(PROTOCOL, SENDER, 0x16, "DebugSampleSubscription");
public static final PacketType B_EDIT = new PacketType(PROTOCOL, SENDER, 0x17, "EditBook", "BEdit");
public static final PacketType ENTITY_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x18, "EntityTagQuery", "EntityNBTQuery");
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x19, "Interact", "UseEntity", "CPacketUseEntity");
public static final PacketType JIGSAW_GENERATE = new PacketType(PROTOCOL, SENDER, 0x1A, "JigsawGenerate");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x1B, "KeepAlive", "CPacketKeepAlive");
public static final PacketType DIFFICULTY_LOCK = new PacketType(PROTOCOL, SENDER, 0x1C, "LockDifficulty", "DifficultyLock");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x1D, "net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Pos", "Flying$Position", "Flying$PacketPlayInPosition", "CPacketPlayer$Position");
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x1E, "net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$PosRot", "Flying$PositionLook", "Flying$PacketPlayInPositionLook", "CPacketPlayer$PositionRotation");
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x1F, "net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Rot", "Flying$Look", "Flying$PacketPlayInLook", "CPacketPlayer$Rotation");
public static final PacketType GROUND = new PacketType(PROTOCOL, SENDER, 0x20, "net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$StatusOnly", "Flying$d");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x21, "MoveVehicle", "VehicleMove", "CPacketVehicleMove");
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x22, "PaddleBoat", "BoatMove", "CPacketSteerBoat");
public static final PacketType PICK_ITEM_FROM_BLOCK = new PacketType(PROTOCOL, SENDER, 0x23, "PickItemFromBlock");
public static final PacketType PICK_ITEM = new PacketType(PROTOCOL, SENDER, 0x24, "PickItemFromEntity", "PickItem");
public static final PacketType PING_REQUEST = new PacketType(PROTOCOL, SENDER, 0x25, "PingRequest");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x26, "PlaceRecipe", "AutoRecipe", "CPacketPlaceRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x27, "PlayerAbilities", "Abilities", "CPacketPlayerAbilities");
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x28, "PlayerAction", "BlockDig", "CPacketPlayerDigging");
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x29, "PlayerCommand", "EntityAction", "CPacketEntityAction");
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x2A, "PlayerInput", "SteerVehicle", "CPacketInput");
public static final PacketType PLAYER_LOADED = new PacketType(PROTOCOL, SENDER, 0x2B, "PlayerLoaded");
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x2C, "Pong");
public static final PacketType RECIPE_SETTINGS = new PacketType(PROTOCOL, SENDER, 0x2D, "RecipeBookChangeSettings", "RecipeSettings");
public static final PacketType RECIPE_DISPLAYED = new PacketType(PROTOCOL, SENDER, 0x2E, "RecipeBookSeenRecipe", "RecipeDisplayed", "CPacketRecipeInfo");
public static final PacketType ITEM_NAME = new PacketType(PROTOCOL, SENDER, 0x2F, "RenameItem", "ItemName");
public static final PacketType RESOURCE_PACK_STATUS = new PacketType(PROTOCOL, SENDER, 0x30, "ResourcePack", "ResourcePackStatus", "CPacketResourcePackStatus");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x31, "SeenAdvancements", "Advancements", "CPacketSeenAdvancements");
public static final PacketType TR_SEL = new PacketType(PROTOCOL, SENDER, 0x32, "SelectTrade", "TrSel");
public static final PacketType BEACON = new PacketType(PROTOCOL, SENDER, 0x33, "SetBeacon", "Beacon");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x34, "SetCarriedItem", "HeldItemSlot", "CPacketHeldItemChange");
public static final PacketType SET_COMMAND_BLOCK = new PacketType(PROTOCOL, SENDER, 0x35, "SetCommandBlock");
public static final PacketType SET_COMMAND_MINECART = new PacketType(PROTOCOL, SENDER, 0x36, "SetCommandMinecart");
public static final PacketType SET_CREATIVE_SLOT = new PacketType(PROTOCOL, SENDER, 0x37, "SetCreativeModeSlot", "SetCreativeSlot", "CPacketCreativeInventoryAction");
public static final PacketType SET_JIGSAW = new PacketType(PROTOCOL, SENDER, 0x38, "SetJigsawBlock", "SetJigsaw");
public static final PacketType STRUCT = new PacketType(PROTOCOL, SENDER, 0x39, "SetStructureBlock", "Struct");
public static final PacketType SET_TEST_BLOCK = new PacketType(PROTOCOL, SENDER, 0x3A, "SetTestBlock");
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x3B, "SignUpdate", "UpdateSign", "CPacketUpdateSign");
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x3C, "Swing", "ArmAnimation", "CPacketAnimation");
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x3D, "TeleportToEntity", "Spectate", "CPacketSpectate");
public static final PacketType TEST_INSTANCE_BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x3E, "TestInstanceBlockAction");
public static final PacketType USE_ITEM_ON = new PacketType(PROTOCOL, SENDER, 0x3F, "UseItemOn", "BlockPlace", "CPacketPlayerTryUseItemOnBlock");
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x40, "UseItem", "CPacketPlayerTryUseItem");
public static final PacketType CUSTOM_CLICK_ACTION = new PacketType(PROTOCOL, SENDER, 0x41, "CustomClickAction");
/**
* @deprecated Removed in 1.17
@@ -609,7 +632,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
@ForceAsync
public static final PacketType DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x00, "LoginDisconnect", "Disconnect", "SPacketDisconnect");
public static final PacketType ENCRYPTION_BEGIN = new PacketType(PROTOCOL, SENDER, 0x01, "Hello", "EncryptionBegin", "SPacketEncryptionRequest");
public static final PacketType SUCCESS = new PacketType(PROTOCOL, SENDER, 0x02, "GameProfile", "Success", "SPacketLoginSuccess");
public static final PacketType SUCCESS = new PacketType(PROTOCOL, SENDER, 0x02, "LoginFinished", "GameProfile", "Success", "SPacketLoginSuccess");
public static final PacketType SET_COMPRESSION = new PacketType(PROTOCOL, SENDER, 0x03, "LoginCompression", "SetCompression", "SPacketEnableCompression");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x04, "CustomQuery", "CustomPayload", "SPacketCustomPayload");
public static final PacketType COOKIE_REQUEST = new PacketType(PROTOCOL, SENDER, 0x05, "CookieRequest");
@@ -688,6 +711,8 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType SELECT_KNOWN_PACKS = new PacketType(PROTOCOL, SENDER, 0x0E, "ClientboundSelectKnownPacks");
public static final PacketType REPORT_DETAILS = new PacketType(PROTOCOL, SENDER, 0x0F, "CustomReportDetails");
public static final PacketType SERVER_LINKS = new PacketType(PROTOCOL, SENDER, 0x10, "ServerLinks");
public static final PacketType CLEAR_DIALOG = new PacketType(PROTOCOL, SENDER, 0x11, "ClearDialog");
public static final PacketType SHOW_DIALOG = new PacketType(PROTOCOL, SENDER, 0x12, "ShowDialog");
/**
* @deprecated Removed in 1.20.4: replaced with new packets for removing and sending resource packs
@@ -719,6 +744,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x05, "Pong");
public static final PacketType RESOURCE_PACK_ACK = new PacketType(PROTOCOL, SENDER, 0x06, "ResourcePack");
public static final PacketType SELECT_KNOWN_PACKS = new PacketType(PROTOCOL, SENDER, 0x07, "ServerboundSelectKnownPacks");
public static final PacketType CUSTOM_CLICK_ACTION = new PacketType(PROTOCOL, SENDER, 0x08, "CustomClickAction");
private static final Client INSTANCE = new Client();
@@ -0,0 +1,57 @@
package com.comphenix.protocol;
import java.lang.reflect.Field;
import java.util.logging.Level;
import com.comphenix.protocol.injector.PacketFilterManager;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import io.netty.channel.Channel;
import io.papermc.paper.connection.PlayerConnection;
import io.papermc.paper.connection.ReadablePlayerCookieConnectionImpl;
import io.papermc.paper.event.connection.PlayerConnectionValidateLoginEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
class PaperLoginListener implements Listener {
private final PacketFilterManager manager;
PaperLoginListener(PacketFilterManager manager) {
this.manager = manager;
}
private static FieldAccessor networkManagerField;
private static FieldAccessor channelField;
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerConnectionValidateLogin(PlayerConnectionValidateLoginEvent event) {
try {
PlayerConnection connection = event.getConnection();
ProtocolLogger.log("onPlayerConnectionValidateLogin: " + connection.getAddress());
if (networkManagerField == null) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(ReadablePlayerCookieConnectionImpl.class, true);
Field field = fuzzy.getFieldByType("networkManager", MinecraftReflection.getNetworkManagerClass());
networkManagerField = Accessors.getFieldAccessor(field);
}
Object networkManager = networkManagerField.get(connection);
if (channelField == null) {
FuzzyReflection fuzzy = FuzzyReflection.fromObject(networkManager, true);
Field field = fuzzy.getFieldByType("channel", Channel.class);
channelField = Accessors.getFieldAccessor(field);
}
Channel channel = (Channel) channelField.get(networkManager);
manager.injectChannel(channel);
ProtocolLogger.log("Successfully injected channel " + channel);
} catch (Throwable ex) {
ProtocolLogger.log(Level.SEVERE, "Failed to inject channel", ex);
}
}
}
@@ -15,26 +15,6 @@
*/
package com.comphenix.protocol;
import com.comphenix.protocol.async.AsyncFilterManager;
import com.comphenix.protocol.error.BasicErrorReporter;
import com.comphenix.protocol.error.DelegatedErrorReporter;
import com.comphenix.protocol.error.DetailedErrorReporter;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.injector.InternalManager;
import com.comphenix.protocol.injector.PacketFilterManager;
import com.comphenix.protocol.metrics.Statistics;
import com.comphenix.protocol.scheduler.DefaultScheduler;
import com.comphenix.protocol.scheduler.FoliaScheduler;
import com.comphenix.protocol.scheduler.ProtocolScheduler;
import com.comphenix.protocol.scheduler.Task;
import com.comphenix.protocol.updater.Updater;
import com.comphenix.protocol.updater.Updater.UpdateType;
import com.comphenix.protocol.utility.*;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -47,9 +27,29 @@ import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import com.comphenix.protocol.async.AsyncFilterManager;
import com.comphenix.protocol.error.BasicErrorReporter;
import com.comphenix.protocol.error.DelegatedErrorReporter;
import com.comphenix.protocol.error.DetailedErrorReporter;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.injector.PacketFilterManager;
import com.comphenix.protocol.metrics.Statistics;
import com.comphenix.protocol.scheduler.DefaultScheduler;
import com.comphenix.protocol.scheduler.FoliaScheduler;
import com.comphenix.protocol.scheduler.ProtocolScheduler;
import com.comphenix.protocol.scheduler.Task;
import com.comphenix.protocol.updater.Updater;
import com.comphenix.protocol.updater.Updater.UpdateType;
import com.comphenix.protocol.utility.ByteBuddyFactory;
import com.comphenix.protocol.utility.ChatExtensions;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.utility.Util;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import org.bukkit.Server;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
@@ -58,7 +58,7 @@ import org.bukkit.plugin.java.JavaPlugin;
*
* @author Kristian
*/
public class ProtocolLib extends JavaPlugin {
public abstract class ProtocolLib extends JavaPlugin {
// Every possible error or warning report type
public static final ReportType REPORT_CANNOT_DELETE_CONFIG = new ReportType(
"Cannot delete old ProtocolLib configuration.");
@@ -91,8 +91,8 @@ public class ProtocolLib extends JavaPlugin {
// these fields are only existing once, we can make them static
private static Logger logger;
private static ProtocolConfig config;
private static InternalManager protocolManager;
private static ErrorReporter reporter = new BasicErrorReporter();
protected static PacketFilterManager protocolManager;
protected static ErrorReporter reporter = new BasicErrorReporter();
private Statistics statistics;
@@ -210,7 +210,7 @@ public class ProtocolLib extends JavaPlugin {
this.commandPacket = new CommandPacket(reporter, this, logger, this.commandFilter, protocolManager);
break;
case LOGGING:
this.packetLogging = new PacketLogging(this, protocolManager);
this.packetLogging = new PacketLogging(reporter, this, protocolManager);
break;
}
} catch (OutOfMemoryError e) {
@@ -336,7 +336,11 @@ public class ProtocolLib extends JavaPlugin {
this.registerCommand(PacketLogging.NAME, this.packetLogging);
// Player login and logout events
protocolManager.registerEvents(manager, this);
protocolManager.inject();
manager.registerEvents(new BukkitListener(protocolManager), this);
registerLoginListener();
// Worker that ensures that async packets are eventually sent
// It also performs the update check.
@@ -364,6 +368,8 @@ public class ProtocolLib extends JavaPlugin {
}
}
abstract void registerLoginListener();
// Used to check Minecraft version
private MinecraftVersion verifyMinecraftVersion() {
MinecraftVersion minimum = new MinecraftVersion(ProtocolLibrary.MINIMUM_MINECRAFT_VERSION);
@@ -437,26 +443,7 @@ public class ProtocolLib extends JavaPlugin {
}
}
private void registerCommand(String name, CommandExecutor executor) {
try {
// Ignore these - they must have printed an error already
if (executor == null) {
return;
}
PluginCommand command = this.getCommand(name);
// Try to load the command
if (command != null) {
command.setExecutor(executor);
} else {
throw new RuntimeException("plugin.yml might be corrupt.");
}
} catch (RuntimeException e) {
reporter.reportWarning(this,
Report.newBuilder(REPORT_CANNOT_REGISTER_COMMAND).messageParam(name, e.getMessage()).error(e));
}
}
abstract void registerCommand(String name, CommandBase command);
/**
* Disable the current plugin.
@@ -504,6 +491,17 @@ public class ProtocolLib extends JavaPlugin {
}
private void checkUpdates() {
// Do not run updater if no supported updater is loaded
if (this.updater == null) {
highlyVisibleError(
" WARNING ",
" ProtocolLib is unable to check for updates. ",
" You are likely not using a supported version (Such as CraftBukkit). "
);
ProtocolLibrary.disableUpdates();
return;
}
// Ignore milliseconds - it's pointless
long currentTime = System.currentTimeMillis() / MILLI_PER_SECOND;
@@ -0,0 +1,19 @@
package com.comphenix.protocol;
public final class ProtocolLibPaper extends ProtocolLib {
@Override
void registerLoginListener() {
try {
Class.forName("io.papermc.paper.event.connection.PlayerConnectionValidateLoginEvent");
getServer().getPluginManager().registerEvents(new com.comphenix.protocol.PaperLoginListener(protocolManager), this);
} catch (ClassNotFoundException ignored) {
getServer().getPluginManager().registerEvents(new LegacyLoginListener(protocolManager), this);
}
}
@Override
public void registerCommand(String name, CommandBase command) {
registerCommand(name, (stack, args) -> command.onCommand(stack.getSender(), args));
}
}
@@ -0,0 +1,30 @@
package com.comphenix.protocol;
import com.comphenix.protocol.error.Report;
import org.bukkit.command.PluginCommand;
public final class ProtocolLibSpigot extends ProtocolLib {
@Override
void registerLoginListener() {
getServer().getPluginManager().registerEvents(new LegacyLoginListener(protocolManager), this);
}
@Override
void registerCommand(String name, CommandBase command) {
try {
PluginCommand pluginCmd = this.getCommand(name);
// Try to load the command
if (pluginCmd != null) {
pluginCmd.setExecutor((sender, _c, _l, args) ->
command.onCommand(sender, args));
} else {
throw new RuntimeException("plugin.yml might be corrupt.");
}
} catch (RuntimeException e) {
reporter.reportWarning(this,
Report.newBuilder(REPORT_CANNOT_REGISTER_COMMAND).messageParam(name, e.getMessage()).error(e));
}
}
}
@@ -18,7 +18,7 @@ import com.comphenix.protocol.error.BasicErrorReporter;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.scheduler.ProtocolScheduler;
import org.apache.commons.lang.Validate;
import com.comphenix.protocol.utility.Validate;
import org.bukkit.plugin.Plugin;
/**
@@ -35,12 +35,12 @@ public class ProtocolLibrary {
/**
* The maximum version ProtocolLib has been tested with.
*/
public static final String MAXIMUM_MINECRAFT_VERSION = "1.21.1";
public static final String MAXIMUM_MINECRAFT_VERSION = "1.21.8";
/**
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.20.4) was released.
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.21.8) was released.
*/
public static final String MINECRAFT_LAST_RELEASE_DATE = "2024-08-08";
public static final String MINECRAFT_LAST_RELEASE_DATE = "2025-07-17";
private static Plugin plugin;
private static ProtocolConfig config;
@@ -48,8 +48,8 @@ public class ProtocolLibrary {
private static ProtocolScheduler scheduler;
private static ErrorReporter reporter = new BasicErrorReporter();
private static boolean updatesDisabled;
private static boolean initialized;
private static boolean updatesDisabled = false;
private static boolean initialized = false;
protected static void init(Plugin plugin, ProtocolConfig config, ProtocolManager manager,
ProtocolScheduler scheduler, ErrorReporter reporter) {
@@ -30,6 +30,8 @@ import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketEvent;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.MinMaxPriorityQueue;
@@ -209,7 +211,7 @@ class PacketProcessingQueue {
public void cleanupAll() {
// Cancel all the threads and every listener
for (AsyncListenerHandler handler : map.values()) {
for (AsyncListenerHandler handler : ImmutableList.copyOf(map.values())) {
handler.cancel();
}
@@ -30,20 +30,17 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.ProtocolConfig;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.collections.ExpireHashMap;
import com.comphenix.protocol.error.Report.ReportBuilder;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.reflect.PrettyPrinter;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Primitives;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
/**
* Internal class used to handle exceptions.
@@ -472,7 +469,7 @@ public class DetailedErrorReporter implements ErrorReporter {
} if (isSimpleType(value) || value instanceof Class<?>) {
return value.toString();
} else {
try {
/*try {
if (!apacheCommonsMissing)
return ToStringBuilder.reflectionToString(value, ToStringStyle.MULTI_LINE_STYLE, false, null);
} catch (LinkageError ex) {
@@ -483,7 +480,7 @@ public class DetailedErrorReporter implements ErrorReporter {
} catch (Throwable ex) {
// Don't use the error logger to log errors in error logging (that could lead to infinite loops)
ProtocolLogger.log(Level.WARNING, "Cannot convert to a String with Apache: " + ex.getMessage());
}
}*/
// Use our custom object printer instead
try {
@@ -10,22 +10,8 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import javax.annotation.Nonnull;
import org.apache.commons.lang.Validate;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.WorldType;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.MerchantRecipe;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
@@ -68,7 +54,20 @@ import com.comphenix.protocol.wrappers.WrappedWatchableObject;
import com.comphenix.protocol.wrappers.nbt.NbtBase;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.google.common.base.Preconditions;
import com.comphenix.protocol.utility.Validate;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.WorldType;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.MerchantRecipe;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
public abstract class AbstractStructure {
protected transient Object handle;
@@ -23,7 +23,7 @@ import java.util.concurrent.TimeUnit;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.apache.commons.lang.Validate;
import com.comphenix.protocol.utility.Validate;
/**
* Stores and retrieves metadata for applicable packet objects.
@@ -36,6 +36,7 @@ import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.ByteBuddyFactory;
import com.comphenix.protocol.utility.Util;
import io.papermc.paper.persistence.PersistentDataContainerView;
import net.bytebuddy.description.ByteCodeElement;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
@@ -60,7 +61,6 @@ import org.bukkit.World;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.profile.PlayerProfile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
@@ -157,11 +157,28 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
return null;
}
/**
* @param b
* @return
*/
@Override
public @org.jspecify.annotations.Nullable Location getRespawnLocation(boolean b) {
return null;
}
@Override
public Location getLocation() {
return null;
}
/**
* @return
*/
@Override
public PersistentDataContainerView getPersistentDataContainer() {
return null;
}
// TODO do we need to implement this?
public void incrementStatistic(Statistic statistic) throws IllegalArgumentException {
@@ -241,8 +258,11 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
return uuid;
}
/**
* @return
*/
@Override
public @NotNull PlayerProfile getPlayerProfile() {
public com.destroystokyo.paper.profile.PlayerProfile getPlayerProfile() {
return null;
}
@@ -288,6 +308,14 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
return online;
}
/**
* @return
*/
@Override
public boolean isConnected() {
return false;
}
@Override
public boolean isWhitelisted() {
return whitelisted;
@@ -39,7 +39,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang.Validate;
import com.comphenix.protocol.utility.Validate;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
@@ -1,8 +1,6 @@
package com.comphenix.protocol.injector;
import com.comphenix.protocol.ProtocolManager;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
/**
* Yields access to the internal hook configuration.
@@ -11,14 +9,6 @@ import org.bukkit.plugin.PluginManager;
*/
public interface InternalManager extends ProtocolManager {
/**
* Register this protocol manager on Bukkit.
*
* @param manager - Bukkit plugin manager that provides player join/leave events.
* @param plugin - the parent plugin.
*/
void registerEvents(PluginManager manager, final Plugin plugin);
/**
* Called when ProtocolLib is closing.
*/
@@ -1,10 +1,12 @@
package com.comphenix.protocol.injector;
import javax.annotation.Nonnull;
import com.comphenix.protocol.ProtocolLib;
import com.comphenix.protocol.async.AsyncFilterManager;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.utility.MinecraftVersion;
import javax.annotation.Nonnull;
import org.bukkit.Server;
import org.bukkit.plugin.Plugin;
@@ -112,7 +114,7 @@ public class PacketFilterBuilder {
*
* @return A new packet filter manager.
*/
public InternalManager build() {
public PacketFilterManager build() {
if (this.reporter == null) {
throw new IllegalArgumentException("reporter cannot be NULL.");
}
@@ -8,21 +8,6 @@ import java.util.Objects;
import java.util.Set;
import java.util.logging.Level;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import com.comphenix.protocol.AsynchronousManager;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
@@ -47,7 +32,15 @@ import com.comphenix.protocol.injector.netty.WirePacket;
import com.comphenix.protocol.injector.netty.manager.NetworkManagerInjector;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.collect.ImmutableSet;
import io.netty.channel.Channel;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
public class PacketFilterManager implements ListenerManager, InternalManager {
@@ -327,24 +320,30 @@ public class PacketFilterManager implements ListenerManager, InternalManager {
@Override
public void removePacketListener(PacketListener listener) {
if (!this.closed && this.registeredListeners.remove(listener)) {
ListeningWhitelist outbound = listener.getSendingWhitelist();
ListeningWhitelist inbound = listener.getReceivingWhitelist();
if (this.closed || !this.registeredListeners.remove(listener)) {
return;
}
// remove outbound listeners (if any)
if (outbound != null && outbound.isEnabled()) {
this.outboundListeners.removeListener(listener);
}
ListeningWhitelist outbound = listener.getSendingWhitelist();
ListeningWhitelist inbound = listener.getReceivingWhitelist();
// remove inbound listeners (if any)
if (inbound != null && inbound.isEnabled()) {
this.inboundListeners.removeListener(listener);
}
// remove outbound listeners (if any)
if (outbound != null && outbound.isEnabled()) {
this.outboundListeners.removeListener(listener);
}
// remove inbound listeners (if any)
if (inbound != null && inbound.isEnabled()) {
this.inboundListeners.removeListener(listener);
}
}
@Override
public void removePacketListeners(Plugin plugin) {
if (this.closed || this.plugin.equals(plugin)) {
return;
}
for (PacketListener listener : this.getPacketListeners()) {
if (Objects.equals(listener.getPlugin(), plugin)) {
this.removePacketListener(listener);
@@ -421,40 +420,26 @@ public class PacketFilterManager implements ListenerManager, InternalManager {
}
}
@Override
public void registerEvents(PluginManager manager, Plugin plugin) {
if (!this.closed && !this.injected) {
// prevent duplicate event registrations / injections
this.injected = true;
this.networkManagerInjector.inject();
public void removePlayer(Player player) {
asyncFilterManager.removePlayer(player);
}
// all listeners we need, this is a bit messy, but it makes the job correctly
manager.registerEvents(new Listener() {
public void injectPlayer(Player player) {
networkManagerInjector.getInjector(player).inject();
}
@EventHandler(priority = EventPriority.LOWEST)
public void handleLogin(PlayerLoginEvent event) {
networkManagerInjector.getInjector(event.getPlayer()).inject();
}
public void injectChannel(Channel channel) {
networkManagerInjector.getInjector(channel).inject();
}
@EventHandler(priority = EventPriority.LOWEST)
public void handleJoin(PlayerJoinEvent event) {
networkManagerInjector.getInjector(event.getPlayer()).inject();
}
@EventHandler(priority = EventPriority.MONITOR)
public void handleQuit(PlayerQuitEvent event) {
PacketFilterManager.this.asyncFilterManager.removePlayer(event.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR)
public void handlePluginUnload(PluginDisableEvent event) {
// don't do this for our plugin!
if (event.getPlugin() != PacketFilterManager.this.plugin) {
PacketFilterManager.this.removePacketListeners(event.getPlugin());
}
}
}, plugin);
public void inject() {
if (this.closed || this.injected) {
return;
}
// prevent duplicate event registrations / injections
this.injected = true;
this.networkManagerInjector.inject();
}
@Override
@@ -2,12 +2,12 @@ package com.comphenix.protocol.injector.netty;
import java.net.SocketAddress;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.events.NetworkMarker;
import org.bukkit.entity.Player;
/**
* Represents an injected client connection.
*
@@ -74,6 +74,8 @@ public interface Injector {
*/
void setPlayer(Player player);
boolean hasValidPlayer();
boolean isConnected();
/**
@@ -2,13 +2,14 @@ package com.comphenix.protocol.injector.netty.channel;
import java.net.SocketAddress;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.injector.netty.Injector;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.comphenix.protocol.injector.temporary.TemporaryPlayer;
import org.bukkit.entity.Player;
final class EmptyInjector implements Injector {
@@ -72,6 +73,11 @@ final class EmptyInjector implements Injector {
this.player = player;
}
@Override
public boolean hasValidPlayer() {
return player != null && !(player instanceof TemporaryPlayer);
}
@Override
public boolean isConnected() {
return false;
@@ -16,12 +16,9 @@ package com.comphenix.protocol.injector.netty.channel;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentMap;
import javax.annotation.Nonnull;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.injector.ListenerManager;
import com.comphenix.protocol.injector.netty.Injector;
@@ -29,10 +26,12 @@ import com.comphenix.protocol.injector.temporary.TemporaryPlayerFactory;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.utility.MinecraftFields;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.MapMaker;
import com.google.common.collect.MapMaker;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
/**
* Represents an injector factory.
@@ -85,12 +84,8 @@ public class InjectionFactory {
// try to get the injector using the player reference first
Injector injector = this.playerLookup.get(player);
if (injector == null) {
injector = this.getTemporaryInjector(player);
}
// check if we found an injector
if (injector != null && !injector.isClosed()) {
ProtocolLogger.log(player.getName() + " has a cached injector. Player valid: " + injector.hasValidPlayer());
return injector;
}
@@ -110,7 +105,10 @@ public class InjectionFactory {
this.playerLookup.remove(injector.getPlayer());
this.cacheInjector(player, injector);
// re-set the player of the injection
ProtocolLogger.log("Reusing existing injector and setting player to " + player.getName());
injector.setPlayer(player);
} else {
ProtocolLogger.log(player.getName() + "'s channel already has an injector. Player valid: " + injector.hasValidPlayer());
}
} else {
// construct a new injector as it seems like we have none yet
@@ -11,9 +11,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicBoolean;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.ProtocolLibrary;
@@ -29,6 +26,7 @@ import com.comphenix.protocol.injector.NetworkProcessor;
import com.comphenix.protocol.injector.netty.Injector;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.injector.temporary.TemporaryPlayer;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
@@ -43,6 +41,8 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoop;
import io.netty.util.AttributeKey;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public class NettyChannelInjector implements Injector {
@@ -289,8 +289,7 @@ public class NettyChannelInjector implements Injector {
this.ensureInEventLoop(() -> {
try {
// try to invoke the method, this should normally not fail
this.listenerInvoker.read(packet);
this.channel.pipeline().context(INBOUND_INTERCEPTOR_NAME).fireChannelRead(packet);
} catch (Exception exception) {
this.errorReporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_READ_PACKET)
.messageParam(packet, this.playerName)
@@ -344,7 +343,7 @@ public class NettyChannelInjector implements Injector {
@Override
public Player getPlayer() {
// if the player was already resolved there is no need to do further lookups
if (this.player != null) {
if (this.player != null && !(this.player instanceof TemporaryPlayer)) {
return this.player;
}
@@ -371,6 +370,11 @@ public class NettyChannelInjector implements Injector {
this.injectionFactory.cacheInjector(player.getName(), this);
}
@Override
public boolean hasValidPlayer() {
return this.player != null && !(player instanceof TemporaryPlayer);
}
@Override
public boolean isConnected() {
return this.channel.isActive();
@@ -14,13 +14,11 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import io.netty.channel.ChannelHandlerContext;
/**
* This class facilitates the invocation of methods on the current packet listener.
* It attempts to execute the <code>send</code>, <code>read</code>, and <code>disconnect</code>
* methods and, upon failure (either due to the absence of the method or the packet
* listener being of an incorrect type), it delegates the call to the network manager.
* It attempts to execute the <code>send</code>, and <code>disconnect</code> methods, upon failure
* (either due to the absence of the method or the packet listener being of an incorrect type),
* it delegates the call to the network manager.
*
* <p>Supported packet listener types include CONFIGURATION and PLAY. If the packet
* listener does not match these types, or if the required method is missing, the
@@ -43,7 +41,6 @@ public class PacketListenerInvoker {
private static final boolean DOES_PACKET_LISTENER_DISCONNECT_USE_COMPONENT = doesPacketListenerDisconnectUseComponent();
private static final MethodAccessor NETWORK_MANAGER_SEND = getNetworkManagerSend();
private static final MethodAccessor NETWORK_MANAGER_READ = getNetworkManagerRead();
private static final MethodAccessor NETWORK_MANAGER_DISCONNECT = getNetworkManagerDisconnect();
private static final MethodAccessor NETWORK_MANAGER_PACKET_LISTENER = getNetworkManagerPacketListener();
@@ -117,15 +114,6 @@ public class PacketListenerInvoker {
return Accessors.getMethodAccessor(send);
}
private static MethodAccessor getNetworkManagerRead() {
FuzzyReflection networkManager = FuzzyReflection.fromClass(MinecraftReflection.getNetworkManagerClass(), true);
Method read = networkManager
.getMethodByParameters("read", ChannelHandlerContext.class, MinecraftReflection.getPacketClass());
return Accessors.getMethodAccessor(read);
}
private static MethodAccessor getNetworkManagerDisconnect() {
FuzzyReflection networkManager = FuzzyReflection.fromClass(MinecraftReflection.getNetworkManagerClass());
@@ -190,7 +178,7 @@ public class PacketListenerInvoker {
}
/**
* Sends a packet using the current packet listener if available and valid; otherwise,
* Sends a packet using the current packet listener if available and valid; otherwise,
* falls back to the network manager.
*
* @param packet The packet to be sent.
@@ -204,15 +192,6 @@ public class PacketListenerInvoker {
}
}
/**
* Reads a packet directly using the network manager.
*
* @param packet The packet to be read.
*/
public void read(Object packet) {
NETWORK_MANAGER_READ.invoke(this.networkManager, null, packet);
}
/**
* Disconnects the player using the current packet listener if available and valid; otherwise,
* falls back to the network manager.
@@ -7,9 +7,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.injector.ListenerManager;
@@ -23,7 +20,10 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.Pair;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
public class NetworkManagerInjector {
@@ -57,6 +57,10 @@ public class NetworkManagerInjector {
return this.injectionFactory.fromPlayer(player);
}
public Injector getInjector(Channel channel) {
return injectionFactory.fromChannel(channel);
}
@SuppressWarnings("unchecked")
public void inject() {
if (this.closed || this.injected) {
@@ -19,8 +19,6 @@ package com.comphenix.protocol.injector.packet;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -31,18 +29,15 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import javax.annotation.Nullable;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.injector.packet.internal.ProtocolRegistry_1_20_5;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyClassContract;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
@@ -70,7 +65,7 @@ public class PacketRegistry {
*
* @author Kristian
*/
private static class Register {
public static class Register {
// The main lookup table
final Map<PacketType, Optional<Class<?>>> typeToClass = new ConcurrentHashMap<>();
@@ -82,14 +77,18 @@ public class PacketRegistry {
volatile Set<PacketType> clientPackets = new HashSet<>();
final List<MapContainer> containers = new ArrayList<>();
public Register() {
private Register() {
}
public void registerPacket(PacketType type, Class<?> clazz, Sender sender) {
typeToClass.put(type, Optional.of(clazz));
public void registerPacket(PacketType type, Class<?> packetClass, Sender sender, WrappedStreamCodec codec) {
typeToClass.put(type, Optional.of(packetClass));
classToType.put(packetClass, type);
classToType.put(clazz, type);
protocolClassToType.computeIfAbsent(type.getProtocol(), __ -> new ConcurrentHashMap<>()).put(clazz, type);
if (codec != null) {
classToCodec.put(packetClass, codec);
}
protocolClassToType.computeIfAbsent(type.getProtocol(), __ -> new ConcurrentHashMap<>()).put(packetClass, type);
if (sender == Sender.CLIENT) {
clientPackets.add(type);
@@ -293,206 +292,9 @@ public class PacketRegistry {
return result;
}
@SuppressWarnings("unchecked")
private static synchronized Register createRegisterV1_20_5() {
Object[] protocols = ENUM_PROTOCOL.getEnumConstants();
// PacketType<?> to class map
final Map<Object, Class<?>> packetTypeMap = new HashMap<>();
// List of all class containing PacketTypes
String[] packetTypesClassNames = new String[] {
"common.CommonPacketTypes",
"configuration.ConfigurationPacketTypes",
"cookie.CookiePacketTypes",
"game.GamePacketTypes",
"handshake.HandshakePacketTypes",
"login.LoginPacketTypes",
"ping.PingPacketTypes",
"status.StatusPacketTypes"
};
Class<?> packetTypeClass = MinecraftReflection.getMinecraftClass("network.protocol.PacketType");
for (String packetTypesClassName : packetTypesClassNames) {
Class<?> packetTypesClass = MinecraftReflection
.getOptionalNMS("network.protocol." + packetTypesClassName)
.orElse(null);
if (packetTypesClass == null) {
ProtocolLogger.debug("Can't find PacketType class: {0}, will skip it", packetTypesClassName);
continue;
}
// check every field for "static final PacketType<?>"
for (Field field : packetTypesClass.getDeclaredFields()) {
try {
if (!Modifier.isFinal(field.getModifiers()) || !Modifier.isStatic(field.getModifiers())) {
continue;
}
Object packetType = field.get(null);
if (!packetTypeClass.isInstance(packetType)) {
continue;
}
// retrieve the generic type T of the PacketType<T> field
Type packet = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
if (packet instanceof Class<?>) {
packetTypeMap.put(packetType, (Class<?>) packet);
}
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
// ID to Packet class maps
final Map<Object, Map<Class<?>, Integer>> serverMaps = new LinkedHashMap<>();
final Map<Object, Map<Class<?>, Integer>> clientMaps = new LinkedHashMap<>();
// global registry instance
final Register result = new Register();
// List of all class containing ProtocolInfos
String[] protocolClassNames = new String[] {
"configuration.ConfigurationProtocols",
"game.GameProtocols",
"handshake.HandshakeProtocols",
"login.LoginProtocols",
"status.StatusProtocols"
};
Class<?> protocolInfoClass = MinecraftReflection.getProtocolInfoClass();
Class<?> protocolInfoUnboundClass = MinecraftReflection.getProtocolInfoUnboundClass();
Class<?> streamCodecClass = MinecraftReflection.getStreamCodecClass();
Class<?> idCodecClass = MinecraftReflection.getMinecraftClass("network.codec.IdDispatchCodec");
Class<?> idCodecEntryClass = MinecraftReflection.getMinecraftClass("network.codec.IdDispatchCodec$Entry", "network.codec.IdDispatchCodec$b");
Class<?> protocolDirectionClass = MinecraftReflection.getPacketFlowClass();
Function<?, ?> emptyFunction = input -> input;
FuzzyReflection protocolInfoReflection = FuzzyReflection.fromClass(protocolInfoClass);
MethodAccessor protocolAccessor = Accessors.getMethodAccessor(protocolInfoReflection
.getMethodByReturnTypeAndParameters("id", MinecraftReflection.getEnumProtocolClass(), new Class[0]));
MethodAccessor directionAccessor = Accessors.getMethodAccessor(protocolInfoReflection
.getMethodByReturnTypeAndParameters("flow", protocolDirectionClass, new Class[0]));
MethodAccessor codecAccessor = Accessors.getMethodAccessor(
protocolInfoReflection.getMethodByReturnTypeAndParameters("codec", streamCodecClass, new Class[0]));
MethodAccessor bindAccessor = Accessors.getMethodAccessor(FuzzyReflection.fromClass(protocolInfoUnboundClass)
.getMethodByReturnTypeAndParameters("bind", protocolInfoClass, new Class[] { Function.class }));
FuzzyReflection idCodecReflection = FuzzyReflection.fromClass(idCodecClass, true);
FieldAccessor byIdAccessor = Accessors.getFieldAccessor(idCodecReflection
.getField(FuzzyFieldContract.newBuilder().typeDerivedOf(List.class).build()));
FieldAccessor toIdAccessor = Accessors.getFieldAccessor(idCodecReflection
.getField(FuzzyFieldContract.newBuilder().typeDerivedOf(Map.class).build()));
FuzzyReflection idCodecEntryReflection = FuzzyReflection.fromClass(idCodecEntryClass, true);
MethodAccessor idCodecEntryTypeAccessor = Accessors.getMethodAccessor(idCodecEntryReflection
.getMethodByReturnTypeAndParameters("type", Object.class, new Class[0]));
MethodAccessor idCodecEntrySerializerAccessor = Accessors.getMethodAccessor(idCodecEntryReflection
.getMethodByReturnTypeAndParameters("serializer", streamCodecClass, new Class[0]));
for (String protocolClassName : protocolClassNames) {
Class<?> protocolClass = MinecraftReflection
.getOptionalNMS("network.protocol." + protocolClassName)
.orElse(null);
if (protocolClass == null) {
ProtocolLogger.debug("Can't find protocol class: {0}, will skip it", protocolClassName);
continue;
}
for (Field field : protocolClass.getDeclaredFields()) {
try {
// ignore none static and final fields
if (!Modifier.isFinal(field.getModifiers()) || !Modifier.isStatic(field.getModifiers())) {
continue;
}
Object protocolInfo = field.get(null);
// bind unbound ProtocolInfo to empty function to get real ProtocolInfo
if (protocolInfoUnboundClass.isInstance(protocolInfo)) {
protocolInfo = bindAccessor.invoke(protocolInfo, new Object[] { emptyFunction });
}
// ignore any field that isn't a ProtocolInfo
if (!protocolInfoClass.isInstance(protocolInfo)) {
continue;
}
// get codec and check if codec is instance of IdDispatchCodec
// since that is the only support codec as of now
Object codec = codecAccessor.invoke(protocolInfo);
if (!idCodecClass.isInstance(codec)) {
continue;
}
// retrieve packetTypeMap and convert it to packetIdMap
Map<Class<?>, Integer> packetMap = new HashMap<>();
List<Object> serializerList = (List<Object>) byIdAccessor.get(codec);
Map<Object, Integer> packetTypeIdMap = (Map<Object, Integer>) toIdAccessor.get(codec);
for (Map.Entry<Object, Integer> entry : packetTypeIdMap.entrySet()) {
Class<?> packetClass = packetTypeMap.get(entry.getKey());
if (packetClass == null) {
throw new RuntimeException("packetType missing packet " + entry.getKey());
}
packetMap.put(packetClass, entry.getValue());
}
// retrieve packet codecs for packet construction and write methods
for (Object entry : serializerList) {
Object packetType = idCodecEntryTypeAccessor.invoke(entry);
Class<?> packetClass = packetTypeMap.get(packetType);
if (packetClass == null) {
throw new RuntimeException("packetType missing packet " + packetType);
}
Object serializer = idCodecEntrySerializerAccessor.invoke(entry);
result.classToCodec.put(packetClass, new WrappedStreamCodec(serializer));
}
// get EnumProtocol and Direction of protocol info
Object protocol = protocolAccessor.invoke(protocolInfo);
String direction = directionAccessor.invoke(protocolInfo).toString();
if (direction.contains("CLIENTBOUND")) { // Sent by Server
serverMaps.put(protocol, packetMap);
} else if (direction.contains("SERVERBOUND")) { // Sent by Client
clientMaps.put(protocol, packetMap);
}
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
for (Object protocol : protocols) {
Enum<?> enumProtocol = (Enum<?>) protocol;
PacketType.Protocol equivalent = PacketType.Protocol.fromVanilla(enumProtocol);
// Associate known types
if (serverMaps.containsKey(protocol)) {
associatePackets(result, reverse(serverMaps.get(protocol)), equivalent, Sender.SERVER);
}
if (clientMaps.containsKey(protocol)) {
associatePackets(result, reverse(clientMaps.get(protocol)), equivalent, Sender.CLIENT);
}
}
ProtocolRegistry_1_20_5.fillRegister(result);
return result;
}
@@ -517,7 +319,7 @@ public class PacketRegistry {
PacketType type = PacketType.fromCurrent(protocol, sender, packetId, packetClass);
try {
register.registerPacket(type, packetClass, sender);
register.registerPacket(type, packetClass, sender, null);
} catch (Exception ex) {
ProtocolLogger.debug("Encountered an exception associating packet " + type, ex);
}
@@ -0,0 +1,71 @@
package com.comphenix.protocol.injector.packet.internal;
import java.util.Arrays;
import java.util.List;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.AbstractWrapper;
import com.comphenix.protocol.wrappers.WrappedStreamCodec;
/**
* Class only works for version 1.20.5+
*/
public class IdCodecWrapper extends AbstractWrapper {
private static final Class<?> ID_CODEC_CLASS;
private static final FieldAccessor BY_ID_ACCESSOR;
private static final Class<?> ID_CODEC_ENTRY_CLASS;
private static final MethodAccessor ENTRY_TYPE_ACCESSOR;
private static final MethodAccessor ENTRY_SERIALIZER_ACCESSOR;
static {
ID_CODEC_CLASS = MinecraftReflection.getMinecraftClass("network.codec.IdDispatchCodec");
BY_ID_ACCESSOR = Accessors.getFieldAccessor(FuzzyReflection.fromClass(ID_CODEC_CLASS, true)
.getField(FuzzyFieldContract.newBuilder().typeDerivedOf(List.class).build()));
ID_CODEC_ENTRY_CLASS = Arrays.stream(ID_CODEC_CLASS.getNestMembers())
.filter(Class::isRecord)
.findFirst()
.orElseThrow(() -> new RuntimeException("Failed to find entry class for: " + ID_CODEC_CLASS));
FuzzyReflection entryReflection = FuzzyReflection.fromClass(ID_CODEC_ENTRY_CLASS, true);
ENTRY_TYPE_ACCESSOR = Accessors.getMethodAccessor(entryReflection
.getMethodByReturnTypeAndParameters("type", Object.class, new Class[0]));
ENTRY_SERIALIZER_ACCESSOR = Accessors.getMethodAccessor(entryReflection
.getMethodByReturnTypeAndParameters("serializer", MinecraftReflection.getStreamCodecClass(), new Class[0]));
}
public IdCodecWrapper(Object handle) {
super(ID_CODEC_CLASS);
setHandle(handle);
}
public List<Entry> getById() {
// list represents a packet-id to {type, codec} lookup
List<?> byId = (List<?>) BY_ID_ACCESSOR.get(this.handle);
return byId.stream().map(Entry::new).toList();
}
public class Entry extends AbstractWrapper {
public Entry(Object handle) {
super(ID_CODEC_ENTRY_CLASS);
setHandle(handle);
}
public Object type() {
return ENTRY_TYPE_ACCESSOR.invoke(this.handle);
}
public WrappedStreamCodec serializer() {
Object serializer = ENTRY_SERIALIZER_ACCESSOR.invoke(this.handle);
return new WrappedStreamCodec(serializer);
}
}
}
@@ -0,0 +1,44 @@
package com.comphenix.protocol.injector.packet.internal;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Class only works for version 1.21.5+
*/
public class ProtocolContexts {
private static final Class<?> GAME_PROTOCOL_CONTEXT_CLASS;
static {
GAME_PROTOCOL_CONTEXT_CLASS = MinecraftReflection.getNullableNMS(
"network.protocol.game.GameProtocols$a" /*Spigot Mapping*/,
"network.protocol.game.GameProtocols$Context" /*Mojang Mapping*/);
}
public static Object createGameProtocolContext() {
return createContext(GAME_PROTOCOL_CONTEXT_CLASS, (proxy, method, args) -> {
if (method.getName().equals("hasInfiniteMaterials") || (method.getReturnType() == boolean.class && method.getParameterCount() == 0)) {
return true;
}
return null;
});
}
private static Object createContext(Class<?> interfaceClass, InvocationHandler handler) {
if (interfaceClass == null) {
return null;
}
return Proxy.newProxyInstance(ProtocolContexts.class.getClassLoader(), new Class[] { interfaceClass }, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return handler.invoke(proxy, method, args);
}
});
}
}
@@ -0,0 +1,102 @@
package com.comphenix.protocol.injector.packet.internal;
import java.util.Objects;
import java.util.function.Function;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.AbstractWrapper;
/**
* Class only works for version 1.20.5+
*/
public class ProtocolInfoWrapper extends AbstractWrapper {
private static final @NotNull Class<?> SIMPLE_UNBOUND_PROTOCOL_CLASS;
private static final @NotNull MethodAccessor BIND_SIMPLE_ACCESSOR;
private static final @Nullable Class<?> UNBOUND_PROTOCOL_CLASS;
private static final @Nullable MethodAccessor BIND_CONTEXT_ACCESSOR;
private static final @NotNull MethodAccessor ID_ACCESSOR;
private static final @NotNull MethodAccessor FLOW_ACCESSOR;
private static final @NotNull MethodAccessor CODEC_ACCESSOR;
static {
SIMPLE_UNBOUND_PROTOCOL_CLASS = MinecraftReflection.getMinecraftClass(
"network.protocol.SimpleUnboundProtocol" /* 1.21.5+ */,
"network.protocol.UnboundProtocol" /* 1.21.8+ Alternative */,
"network.ProtocolInfo$a" /* Spigot Mappings */,
"network.ProtocolInfo$Unbound" /* Mojang Mappings */);
BIND_SIMPLE_ACCESSOR = Accessors.getMethodAccessor(FuzzyReflection.fromClass(SIMPLE_UNBOUND_PROTOCOL_CLASS)
.getMethodByReturnTypeAndParameters("bind", MinecraftReflection.getProtocolInfoClass(), new Class[] { Function.class }));
UNBOUND_PROTOCOL_CLASS = MinecraftReflection.getNullableNMS("network.protocol.UnboundProtocol");
if (UNBOUND_PROTOCOL_CLASS != null) {
BIND_CONTEXT_ACCESSOR = Accessors.getMethodAccessor(FuzzyReflection.fromClass(UNBOUND_PROTOCOL_CLASS)
.getMethodByReturnTypeAndParameters("bind", MinecraftReflection.getProtocolInfoClass(), new Class[] { Function.class, Object.class }));
} else {
BIND_CONTEXT_ACCESSOR = null;
}
FuzzyReflection protocolInfoReflection = FuzzyReflection.fromClass(MinecraftReflection.getProtocolInfoClass());
ID_ACCESSOR = Accessors.getMethodAccessor(protocolInfoReflection
.getMethodByReturnTypeAndParameters("id", MinecraftReflection.getEnumProtocolClass(), new Class[0]));
FLOW_ACCESSOR = Accessors.getMethodAccessor(protocolInfoReflection
.getMethodByReturnTypeAndParameters("flow", MinecraftReflection.getPacketFlowClass(), new Class[0]));
CODEC_ACCESSOR = Accessors.getMethodAccessor(protocolInfoReflection
.getMethodByReturnTypeAndParameters("codec", MinecraftReflection.getStreamCodecClass(), new Class[0]));
}
public static boolean isUnboundProtocol(Object protocol) {
return SIMPLE_UNBOUND_PROTOCOL_CLASS.isInstance(protocol) || (UNBOUND_PROTOCOL_CLASS != null && UNBOUND_PROTOCOL_CLASS.isInstance(protocol));
}
public static ProtocolInfoWrapper fromUnbound(Object protocol, Object context) {
// bind to identity function in-order to map directly to FriendlyByteBuf instead of ByteBuf
if (SIMPLE_UNBOUND_PROTOCOL_CLASS.isInstance(protocol)) {
Object protocolInfo = BIND_SIMPLE_ACCESSOR.invoke(protocol, Function.identity());
return new ProtocolInfoWrapper(protocolInfo);
}
if (UNBOUND_PROTOCOL_CLASS != null && UNBOUND_PROTOCOL_CLASS.isInstance(protocol)) {
Object protocolInfo = BIND_CONTEXT_ACCESSOR.invoke(protocol, Function.identity(), Objects.requireNonNull(context));
return new ProtocolInfoWrapper(protocolInfo);
}
throw new RuntimeException("Unknown protocol implementation: " + protocol);
}
private ProtocolInfoWrapper(Object handle) {
super(MinecraftReflection.getProtocolInfoClass());
setHandle(handle);
}
public PacketType.Protocol id() {
Object protocol = ID_ACCESSOR.invoke(this.handle);
return PacketType.Protocol.fromVanilla((Enum<?>) protocol);
}
public PacketType.Sender flow() {
String packetFlow = FLOW_ACCESSOR.invoke(this.handle).toString();
if (packetFlow.contains("CLIENTBOUND")) {
return PacketType.Sender.SERVER;
} else if (packetFlow.contains("SERVERBOUND")) {
return PacketType.Sender.CLIENT;
}
throw new RuntimeException("Unknown packet flow: " + packetFlow);
}
public IdCodecWrapper codec() {
return new IdCodecWrapper(CODEC_ACCESSOR.invoke(this.handle));
}
}
@@ -0,0 +1,150 @@
package com.comphenix.protocol.injector.packet.internal;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.injector.packet.PacketRegistry.Register;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Class only works for version 1.20.5+
*/
public class ProtocolRegistry_1_20_5 {
private static final Class<?> PACKET_TYPE_CLASS = MinecraftReflection.getMinecraftClass("network.protocol.PacketType");;
/**
* List of all class containing PacketTypes
*/
public static final List<String> PACKET_TYPES_CLASS_NAMES = List.of(
"common.CommonPacketTypes",
"configuration.ConfigurationPacketTypes",
"cookie.CookiePacketTypes",
"game.GamePacketTypes",
"handshake.HandshakePacketTypes",
"login.LoginPacketTypes",
"ping.PingPacketTypes",
"status.StatusPacketTypes");
/**
* List of all class containing ProtocolInfos
*/
public static final List<Protocol> PROTOCOLS = List.of(
new Protocol("configuration.ConfigurationProtocols", null),
new Protocol("game.GameProtocols", ProtocolContexts::createGameProtocolContext),
new Protocol("handshake.HandshakeProtocols", null),
new Protocol("login.LoginProtocols", null),
new Protocol("status.StatusProtocols", null));
public static void fillRegister(Register register) {
final Map<Object, Class<?>> packetTypeMap = getPacketTypes();
for (Protocol protocol : PROTOCOLS) {
Class<?> protocolClass = MinecraftReflection.getNullableNMS("network.protocol." + protocol.className());
if (protocolClass == null) {
ProtocolLogger.debug("Can't find protocol class: {0}, will skip it", protocol);
continue;
}
for (Field field : protocolClass.getDeclaredFields()) {
try {
// ignore none static and final fields
if (!Modifier.isFinal(field.getModifiers()) || !Modifier.isStatic(field.getModifiers())) {
continue;
}
Object fieldValue = field.get(null);
// Skip any field that isn't a unbound ProtocolInfo.
// We need to bind the ProtocolInfo ourself as we apply our own stream mapping
// inside the ProtocolInfoWrapper::fromUnbound method
if (!ProtocolInfoWrapper.isUnboundProtocol(fieldValue)) {
continue;
}
// create optional context object to bind protocol to
Object context = protocol.context() != null
? protocol.context().get()
: null;
// wrap bound protocol info for ease of use
ProtocolInfoWrapper protocolInfo = ProtocolInfoWrapper.fromUnbound(fieldValue, context);
PacketType.Protocol packetProtocol = protocolInfo.id();
PacketType.Sender packetSender = protocolInfo.flow();
// register each packet to Register instance
List<IdCodecWrapper.Entry> entries = protocolInfo.codec().getById();
for (int id = 0; id < entries.size(); id++) {
IdCodecWrapper.Entry entry = entries.get(id);
Class<?> packetClass = packetTypeMap.get(entry.type());
if (packetClass == null) {
throw new RuntimeException("packetType missing packet " + entry.type());
}
PacketType packetType = PacketType.fromCurrent(packetProtocol, packetSender, id, packetClass);
// TODO(fix): validate that every codec expects FriendlyByteBuf
register.registerPacket(packetType, packetClass, packetSender, entry.serializer());
}
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
}
/**
* Returns a PacketType to packet class lookup map
*/
private static Map<Object, Class<?>> getPacketTypes() {
Map<Object, Class<?>> packetTypeMap = new HashMap<>();
for (String packetTypesClassName : PACKET_TYPES_CLASS_NAMES) {
Class<?> packetTypesClass = MinecraftReflection.getNullableNMS("network.protocol." + packetTypesClassName);
if (packetTypesClass == null) {
ProtocolLogger.debug("Can't find PacketType class: {0}, will skip it", packetTypesClassName);
continue;
}
// check every field for "static final PacketType<?>"
for (Field field : packetTypesClass.getDeclaredFields()) {
try {
if (!Modifier.isFinal(field.getModifiers()) || !Modifier.isStatic(field.getModifiers())) {
continue;
}
Object packetType = field.get(null);
if (!PACKET_TYPE_CLASS.isInstance(packetType)) {
continue;
}
// retrieve the generic type T of the PacketType<T> field
Type packet = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
if (packet instanceof Class<?>) {
packetTypeMap.put(packetType, (Class<?>) packet);
}
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
return packetTypeMap;
}
private record Protocol(@NotNull String className, @Nullable Supplier<Object> context) {
}
}
@@ -93,6 +93,9 @@ public final class MinecraftProtocolVersion {
map.put(new MinecraftVersion(1, 20, 5), 766);
map.put(new MinecraftVersion(1, 21, 0), 767);
map.put(new MinecraftVersion(1, 21, 2), 768);
map.put(new MinecraftVersion(1, 21, 6), 771);
map.put(new MinecraftVersion(1, 21, 8), 772);
return map;
}
@@ -685,7 +685,8 @@ public final class MinecraftReflection {
* @throws IllegalStateException If the class could not be found or deduced.
*/
public static Class<?> getChatSerializerClass() {
return getMinecraftClass("network.chat.IChatBaseComponent$ChatSerializer", "network.chat.Component$Serializer", "IChatBaseComponent$ChatSerializer");
return getMinecraftClass("network.chat.IChatBaseComponent$ChatSerializer",
"network.chat.Component$Serializer", "network.chat.ComponentSerialization", "IChatBaseComponent$ChatSerializer");
}
/**
@@ -1898,4 +1899,16 @@ public final class MinecraftReflection {
return isMojangMapped;
}
public static Optional<Class<?>> getDataComponentMapClass() {
return getOptionalNMS("core.component.DataComponentMap");
}
public static Optional<Class<?>> getDataComponentHolderClass() {
return getOptionalNMS("core.component.DataComponentHolder");
}
public static Optional<Class<?>> getTypedDataComponentClass() {
return getOptionalNMS("core.component.TypedDataComponent");
}
}
@@ -2,6 +2,7 @@ package com.comphenix.protocol.utility;
import java.lang.reflect.Modifier;
import com.comphenix.protocol.wrappers.codecs.WrappedDynamicOps;
import org.bukkit.Bukkit;
import com.comphenix.protocol.reflect.FuzzyReflection;
@@ -17,6 +18,7 @@ public class MinecraftRegistryAccess {
private static MethodAccessor GET_SERVER = null;
private static MethodAccessor REGISTRY_ACCESS = null;
private static MethodAccessor CREATE_SERIALIZATION_CONTEXT = null;
// lazy initialized
private static Object registryAccess = null;
@@ -36,6 +38,14 @@ public class MinecraftRegistryAccess {
.banModifier(Modifier.STATIC)
.returnDerivedOf(MinecraftReflection.getRegistryAccessClass())
.build()));
CREATE_SERIALIZATION_CONTEXT = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(MinecraftReflection.getHolderLookupProviderClass(), false)
.getMethod(FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.returnDerivedOf(MinecraftReflection.getDynamicOpsClass())
.parameterDerivedOf(MinecraftReflection.getDynamicOpsClass())
.build()));
}
}
@@ -57,4 +67,13 @@ public class MinecraftRegistryAccess {
return registryAccess;
}
/**
* Returns a RegistryOps using the given DynamicOps as the underlying ops.
*
* @return a RegistryOps
*/
public static WrappedDynamicOps createSerializationContext(WrappedDynamicOps dynamicOps) {
return WrappedDynamicOps.fromHandle(CREATE_SERIALIZATION_CONTEXT.invoke(get(), dynamicOps.getHandle()));
}
}
@@ -36,6 +36,26 @@ import org.bukkit.Server;
* @author Kristian
*/
public final class MinecraftVersion implements Comparable<MinecraftVersion>, Serializable {
/**
* Version 1.21.6 - chase the skies
*/
public static final MinecraftVersion v1_21_6 = new MinecraftVersion("1.21.6");
/**
* Version 1.21.5 - spring to life drop
*/
public static final MinecraftVersion v1_21_5 = new MinecraftVersion("1.21.5");
/**
* Version 1.21.4 - the garden awakens drop
*/
public static final MinecraftVersion v1_21_4 = new MinecraftVersion("1.21.4");
/**
* Version 1.21.2 - the bundles of bravery drop
*/
public static final MinecraftVersion v1_21_2 = new MinecraftVersion("1.21.2");
/**
* Version 1.21.0 - the tricky trials update
*/
@@ -149,7 +169,7 @@ public final class MinecraftVersion implements Comparable<MinecraftVersion>, Ser
/**
* The latest release version of minecraft.
*/
public static final MinecraftVersion LATEST = v1_21_0;
public static final MinecraftVersion LATEST = v1_21_6;
// used when serializing
private static final long serialVersionUID = -8695133558996459770L;
@@ -0,0 +1,17 @@
package com.comphenix.protocol.utility;
public final class Validate {
private Validate() {}
public static void notNull(Object obj, String err) {
if (obj == null) {
throw new IllegalArgumentException(err);
}
}
public static void isTrue(boolean bool, String err) {
if (!bool) {
throw new IllegalArgumentException(err);
}
}
}
@@ -0,0 +1,12 @@
package com.comphenix.protocol.utility;
public final class WordUtils {
private WordUtils() {}
public static String capitalize(String str) {
if (str == null || str.isEmpty()) {
return str;
}
return str.substring(0, 1).toUpperCase() + str.substring(1).toLowerCase();
}
}
@@ -11,7 +11,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang.Validate;
import com.comphenix.protocol.utility.Validate;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
@@ -140,6 +140,8 @@ public abstract class EnumWrappers {
UPDATE_LISTED,
UPDATE_LATENCY,
UPDATE_DISPLAY_NAME,
UPDATE_LIST_ORDER,
UPDATE_HAT,
/**
* @deprecated Removed in 1.19.3
*/
@@ -191,8 +193,18 @@ public abstract class EnumWrappers {
}
public enum PlayerAction implements AliasedEnum {
/**
* @deprecated Removed in 1.21.6
*/
@Deprecated
START_SNEAKING("PRESS_SHIFT_KEY"),
/**
* @deprecated Removed in 1.21.6
*/
@Deprecated
STOP_SNEAKING("RELEASE_SHIFT_KEY"),
STOP_SLEEPING,
START_SPRINTING,
STOP_SPRINTING,
@@ -332,7 +344,8 @@ public abstract class EnumWrappers {
NEUTRAL("neutral"),
PLAYERS("player"),
AMBIENT("ambient"),
VOICE("voice");
VOICE("voice"),
UI("ui");
private static final Map<String, SoundCategory> LOOKUP;
static {
@@ -364,7 +377,8 @@ public abstract class EnumWrappers {
LEGS,
CHEST,
HEAD,
BODY
BODY,
SADDLE
}
public enum Hand {
@@ -638,6 +652,71 @@ public abstract class EnumWrappers {
TRANSFER;
}
public enum TeamCollisionRule {
ALWAYS("always"),
NEVER("never"),
PUSH_OTHER_TEAMS("pushOtherTeams"),
PUSH_OWN_TEAM("pushOwnTeam");
public static TeamCollisionRule fromName(String name) {
for (TeamCollisionRule value : values()) {
if (value.name.equals(name)) {
return value;
}
}
throw new IllegalArgumentException("Unknown team collision rule: " + name);
}
private final String name;
TeamCollisionRule(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
public enum TeamVisibility {
ALWAYS("always"),
NEVER("never"),
HIDE_FOR_OTHER_TEAMS("hideForOtherTeams"),
HIDE_FOR_OWN_TEAM("hideForOwnTeam");
public static TeamVisibility fromName(String name) {
for (TeamVisibility value : values()) {
if (value.name.equals(name)) {
return value;
}
}
throw new IllegalArgumentException("Unknown team visibility: " + name);
}
private final String name;
TeamVisibility(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
public enum HeightmapType {
WORLD_SURFACE_WG,
WORLD_SURFACE,
OCEAN_FLOOR_WG,
OCEAN_FLOOR,
MOTION_BLOCKING,
MOTION_BLOCKING_NO_LEAVES,
}
private static Class<?> PROTOCOL_CLASS = null;
private static Class<?> CLIENT_COMMAND_CLASS = null;
private static Class<?> CHAT_VISIBILITY_CLASS = null;
@@ -663,6 +742,9 @@ public abstract class EnumWrappers {
private static Class<?> RENDER_TYPE_CLASS = null;
private static Class<?> CHAT_FORMATTING_CLASS = null;
private static Class<?> CLIENT_INTENT_CLASS = null;
private static Class<?> TEAM_COLLISION_RULE_CLASS = null;
private static Class<?> TEAM_VISIBILITY_CLASS = null;
private static Class<?> HEIGHTMAP_TYPE_CLASS = null;
private static boolean INITIALIZING = false;
private static boolean INITIALIZED = false;
@@ -770,6 +852,17 @@ public abstract class EnumWrappers {
CLIENT_INTENT_CLASS = getEnum(PacketType.Handshake.Client.SET_PROTOCOL.getPacketClass(), 0);
TEAM_COLLISION_RULE_CLASS = MinecraftReflection.getNullableNMS(
"world.scores.ScoreboardTeamBase$EnumTeamPush" /* Spigot Mapping */,
"world.scores.Team$CollisionRule" /* Mojang Mapping */);
TEAM_VISIBILITY_CLASS = MinecraftReflection.getNullableNMS(
"world.scores.ScoreboardTeamBase$EnumNameTagVisibility" /* Spigot Mapping */,
"world.scores.Team$Visibility" /* Mojang Mapping */);
HEIGHTMAP_TYPE_CLASS = MinecraftReflection.getNullableNMS(
"world.level.levelgen.HeightMap$Type" /* Spigot Mapping */,
"world.level.levelgen.Heightmap$Types" /* Mojang Mapping */);
associate(PROTOCOL_CLASS, Protocol.class, getProtocolConverter());
associate(CLIENT_COMMAND_CLASS, ClientCommand.class, getClientCommandConverter());
associate(CHAT_VISIBILITY_CLASS, ChatVisibility.class, getChatVisibilityConverter());
@@ -794,6 +887,9 @@ public abstract class EnumWrappers {
associate(RENDER_TYPE_CLASS, RenderType.class, getRenderTypeConverter());
associate(CHAT_FORMATTING_CLASS, ChatFormatting.class, getChatFormattingConverter());
associate(CLIENT_INTENT_CLASS, ClientIntent.class, getClientIntentConverter());
associate(TEAM_COLLISION_RULE_CLASS, TeamCollisionRule.class, getTeamCollisionRuleConverter());
associate(TEAM_VISIBILITY_CLASS, TeamVisibility.class, getTeamVisibilityConverter());
associate(HEIGHTMAP_TYPE_CLASS, HeightmapType.class, getHeightmapTypeConverter());
if (ENTITY_POSE_CLASS != null) {
associate(ENTITY_POSE_CLASS, EntityPose.class, getEntityPoseConverter());
@@ -968,6 +1064,21 @@ public abstract class EnumWrappers {
return CLIENT_INTENT_CLASS;
}
public static Class<?> getTeamCollisionRuleClass() {
initialize();
return TEAM_COLLISION_RULE_CLASS;
}
public static Class<?> getTeamVisibilityClass() {
initialize();
return TEAM_VISIBILITY_CLASS;
}
public static Class<?> getHeightmapTypeClass() {
initialize();
return HEIGHTMAP_TYPE_CLASS;
}
// Get the converters
public static EquivalentConverter<Protocol> getProtocolConverter() {
return new EnumConverter<>(getProtocolClass(), Protocol.class);
@@ -1065,6 +1176,18 @@ public abstract class EnumWrappers {
return new EnumConverter<>(getClientIntentClass(), ClientIntent.class);
}
public static EquivalentConverter<TeamCollisionRule> getTeamCollisionRuleConverter() {
return new EnumConverter<>(getTeamCollisionRuleClass(), TeamCollisionRule.class);
}
public static EquivalentConverter<TeamVisibility> getTeamVisibilityConverter() {
return new EnumConverter<>(getTeamVisibilityClass(), TeamVisibility.class);
}
public static EquivalentConverter<HeightmapType> getHeightmapTypeConverter() {
return new EnumConverter<>(getHeightmapTypeClass(), HeightmapType.class);
}
/**
* @since 1.13+
* @return {@link EnumConverter} or null (if bellow 1.13 / nms EnumPose class cannot be found)
@@ -37,7 +37,7 @@ import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
import com.comphenix.protocol.wrappers.collection.ConvertedMap;
import org.apache.commons.lang.Validate;
import com.comphenix.protocol.utility.Validate;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
@@ -53,7 +53,14 @@ public class MinecraftKey {
* @param key The key
*/
public MinecraftKey(String key) {
this("minecraft", key);
if (key.contains(":")) {
String[] split = key.split(":", 2);
this.prefix = split[0];
this.key = split[1];
} else {
this.prefix = "minecraft";
this.key = key;
}
}
/**
@@ -16,12 +16,12 @@
*/
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import javax.annotation.Nullable;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.reflect.EquivalentConverter;
@@ -29,8 +29,7 @@ import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
import javax.annotation.Nullable;
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
/**
* Represents an immutable PlayerInfoData in the PLAYER_INFO packet.
@@ -41,7 +40,9 @@ public class PlayerInfoData {
private final UUID profileId;
private final int latency;
private final int listOrder;
private final boolean listed;
private final boolean showHat;
private final NativeGameMode gameMode;
private final WrappedGameProfile profile;
private final WrappedChatComponent displayName;
@@ -74,6 +75,21 @@ public class PlayerInfoData {
this(profileId, latency, listed, gameMode, profile, displayName, (WrappedRemoteChatSessionData) null);
}
/**
* Constructs a new PlayerInfoData for Minecraft 1.19.3 or later.
*
* @param profileId the id of the profile (has to be non-null)
* @param latency the latency in milliseconds
* @param listed whether the player is listed in the tab list
* @param gameMode the game mode
* @param profile the game profile
* @param displayName display name in tab list (optional)
* @param remoteChatSession the remote chat session for this profile or null
*/
public PlayerInfoData(UUID profileId, int latency, boolean listed, NativeGameMode gameMode, WrappedGameProfile profile, WrappedChatComponent displayName, @Nullable WrappedRemoteChatSessionData remoteChatSession) {
this(profileId, latency, listed, gameMode, profile, displayName, false, remoteChatSession);
}
/**
* Constructs a new PlayerInfoData for Minecraft 1.19. This is incompatible on 1.19.3.
* @see PlayerInfoData#PlayerInfoData(UUID, int, boolean, NativeGameMode, WrappedGameProfile, WrappedChatComponent, WrappedRemoteChatSessionData)
@@ -96,10 +112,12 @@ public class PlayerInfoData {
this.displayName = displayName;
this.profileKeyData = profileKeyData;
this.remoteChatSessionData = null;
this.showHat = false;
this.listOrder = 0;
}
/**
* Constructs a new PlayerInfoData for Minecraft 1.19.3 or later.
* Constructs a new PlayerInfoData for Minecraft 1.21.2 or later.
*
* @param profileId the id of the profile (has to be non-null)
* @param latency the latency in milliseconds
@@ -107,9 +125,43 @@ public class PlayerInfoData {
* @param gameMode the game mode
* @param profile the game profile
* @param displayName display name in tab list (optional)
* @param listOrder the priority of this entry in the tab list
* @param remoteChatSession the remote chat session for this profile or null
*/
public PlayerInfoData(UUID profileId, int latency, boolean listed, NativeGameMode gameMode, WrappedGameProfile profile, WrappedChatComponent displayName, @Nullable WrappedRemoteChatSessionData remoteChatSession) {
public PlayerInfoData(UUID profileId, int latency, boolean listed, NativeGameMode gameMode, WrappedGameProfile profile, WrappedChatComponent displayName, int listOrder, @Nullable WrappedRemoteChatSessionData remoteChatSession) {
this(profileId, latency, listed, gameMode, profile, displayName, false, listOrder, remoteChatSession);
}
/**
* Constructs a new PlayerInfoData for Minecraft 1.21.4 or later.
*
* @param profileId the id of the profile (has to be non-null)
* @param latency the latency in milliseconds
* @param listed whether the player is listed in the tab list
* @param gameMode the game mode
* @param profile the game profile
* @param displayName display name in tab list (optional)
* @param showHat whether a hat should be shown
* @param remoteChatSession the remote chat session for this profile or null
*/
public PlayerInfoData(UUID profileId, int latency, boolean listed, NativeGameMode gameMode, WrappedGameProfile profile, WrappedChatComponent displayName, boolean showHat, @Nullable WrappedRemoteChatSessionData remoteChatSession) {
this(profileId, latency, listed, gameMode, profile, displayName, showHat, 0, remoteChatSession);
}
/**
* Constructs a new PlayerInfoData for Minecraft 1.21.4 or later.
*
* @param profileId the id of the profile (has to be non-null)
* @param latency the latency in milliseconds
* @param listed whether the player is listed in the tab list
* @param gameMode the game mode
* @param profile the game profile
* @param displayName display name in tab list (optional)
* @param showHat whether a hat should be shown
* @param listOrder the priority of this entry in the tab list
* @param remoteChatSession the remote chat session for this profile or null
*/
public PlayerInfoData(UUID profileId, int latency, boolean listed, NativeGameMode gameMode, WrappedGameProfile profile, WrappedChatComponent displayName, boolean showHat, int listOrder, @Nullable WrappedRemoteChatSessionData remoteChatSession) {
this.profileId = profileId;
this.latency = latency;
this.listed = listed;
@@ -118,6 +170,8 @@ public class PlayerInfoData {
this.displayName = displayName;
this.profileKeyData = null;
this.remoteChatSessionData = remoteChatSession;
this.showHat = showHat;
this.listOrder = listOrder;
}
/**
@@ -179,6 +233,22 @@ public class PlayerInfoData {
return displayName;
}
/**
* Gets if a hat is shown (since 1.21.4)
* @return if the hat is shown
*/
public boolean isShowHat() {
return showHat;
}
/**
* Gets the priority of this entry in the tab list (since 1.21.2)
* @return the priority of this entry in the tab list
*/
public int getListOrder() {
return listOrder;
}
/**
* Returns the public key of the profile (since 1.19). Returns the public key of the remote chat session since 1.19.3
* @return The public key of the profile.
@@ -202,7 +272,9 @@ public class PlayerInfoData {
* @return A new converter.
*/
public static EquivalentConverter<PlayerInfoData> getConverter() {
return new EquivalentConverter<PlayerInfoData>() {
return new EquivalentConverter<>() {
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
@Override
public Object getGeneric(PlayerInfoData specific) {
if (constructor == null) {
@@ -225,13 +297,20 @@ public class PlayerInfoData {
args.add(EnumWrappers.getGameModeClass());
args.add(MinecraftReflection.getIChatBaseComponentClass());
if (MinecraftVersion.v1_21_4.atOrAbove()) {
args.add(boolean.class);
}
if (MinecraftVersion.v1_21_2.atOrAbove()) {
args.add(int.class);
}
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
args.add(MinecraftReflection.getRemoteChatSessionDataClass());
} else if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
args.add(MinecraftReflection.getProfilePublicKeyDataClass());
}
constructor = MinecraftReflection.getPlayerInfoDataClass().getConstructor(args.toArray(new Class<?>[0]));
constructor = MinecraftReflection.getPlayerInfoDataClass().getConstructor(args.toArray(EMPTY_CLASS_ARRAY));
} catch (Exception e) {
throw new RuntimeException("Cannot find PlayerInfoData constructor.", e);
}
@@ -240,32 +319,72 @@ public class PlayerInfoData {
// Attempt to construct the underlying PlayerInfoData
try {
Object gameMode = EnumWrappers.getGameModeConverter().getGeneric(specific.gameMode);
Object gameMode = Converters.ignoreNull(EnumWrappers.getGameModeConverter()).getGeneric(specific.gameMode);
Object displayName = specific.displayName != null ? specific.displayName.handle : null;
Object profile = specific.profile != null ? specific.profile.handle : null;
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
return constructor.newInstance(
specific.profileId,
profile,
specific.listed,
specific.latency,
gameMode,
displayName,
specific.remoteChatSessionData != null ? BukkitConverters.getWrappedRemoteChatSessionDataConverter().getGeneric(specific.remoteChatSessionData) : null
);
Object remoteChatSessionData = specific.remoteChatSessionData != null ? BukkitConverters.getWrappedRemoteChatSessionDataConverter().getGeneric(specific.remoteChatSessionData) : null;
Object[] args;
if (MinecraftVersion.v1_21_4.atOrAbove()) {
args = new Object[] {
specific.profileId,
profile,
specific.listed,
specific.latency,
gameMode,
displayName,
specific.showHat,
specific.listOrder,
remoteChatSessionData
};
} else if (MinecraftVersion.v1_21_2.atOrAbove()) {
args = new Object[] {
specific.profileId,
profile,
specific.listed,
specific.latency,
gameMode,
displayName,
specific.listOrder,
remoteChatSessionData
};
} else if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
args = new Object[] {
specific.profileId,
profile,
specific.listed,
specific.latency,
gameMode,
displayName,
remoteChatSessionData
};
} else if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
return constructor.newInstance(
profile,
specific.latency,
gameMode,
displayName,
specific.profileKeyData == null ? null : specific.profileKeyData.handle);
args = new Object[] {
profile,
specific.latency,
gameMode,
displayName,
specific.profileKeyData == null ? null : specific.profileKeyData.handle
};
} else if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
return constructor.newInstance(profile, specific.latency, gameMode, displayName);
args = new Object[] {
profile,
specific.latency,
gameMode,
displayName
};
} else {
return constructor.newInstance(null, profile, specific.latency, gameMode, displayName);
args = new Object[] {
null,
profile,
specific.latency,
gameMode,
displayName
};
}
return constructor.newInstance(args);
} catch (Exception e) {
throw new RuntimeException("Failed to construct PlayerInfoData.", e);
}
@@ -275,37 +394,60 @@ public class PlayerInfoData {
public PlayerInfoData getSpecific(Object generic) {
if (MinecraftReflection.isPlayerInfoData(generic)) {
StructureModifier<Object> modifier = new StructureModifier<>(generic.getClass(), null, false)
.withTarget(generic);
.withTarget(generic);
StructureModifier<WrappedGameProfile> gameProfiles = modifier.withType(
MinecraftReflection.getGameProfileClass(), BukkitConverters.getWrappedGameProfileConverter());
MinecraftReflection.getGameProfileClass(), BukkitConverters.getWrappedGameProfileConverter());
WrappedGameProfile gameProfile = gameProfiles.read(0);
StructureModifier<Integer> ints = modifier.withType(int.class);
int latency = ints.read(0);
StructureModifier<NativeGameMode> gameModes = modifier.withType(
EnumWrappers.getGameModeClass(), EnumWrappers.getGameModeConverter());
EnumWrappers.getGameModeClass(), Converters.ignoreNull(EnumWrappers.getGameModeConverter()));
NativeGameMode gameMode = gameModes.read(0);
StructureModifier<WrappedChatComponent> displayNames = modifier.withType(
MinecraftReflection.getIChatBaseComponentClass(), BukkitConverters.getWrappedChatComponentConverter());
MinecraftReflection.getIChatBaseComponentClass(), BukkitConverters.getWrappedChatComponentConverter());
WrappedChatComponent displayName = displayNames.read(0);
if(MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
if (MinecraftVersion.v1_21_4.atOrAbove()) {
return new PlayerInfoData(modifier.<UUID>withType(UUID.class).read(0),
latency,
modifier.<Boolean>withType(boolean.class).read(0),
gameMode,
gameProfile,
displayName,
modifier.withType(MinecraftReflection.getRemoteChatSessionDataClass(), BukkitConverters.getWrappedRemoteChatSessionDataConverter()).read(0)
);
latency,
modifier.<Boolean>withType(boolean.class).read(0),
gameMode,
gameProfile,
displayName,
modifier.<Boolean>withType(boolean.class).read(1),
ints.read(1),
modifier.withType(MinecraftReflection.getRemoteChatSessionDataClass(), BukkitConverters.getWrappedRemoteChatSessionDataConverter()).read(0)
);
}
if (MinecraftVersion.v1_21_2.atOrAbove()) {
return new PlayerInfoData(modifier.<UUID>withType(UUID.class).read(0),
latency,
modifier.<Boolean>withType(boolean.class).read(0),
gameMode,
gameProfile,
displayName,
ints.read(1),
modifier.withType(MinecraftReflection.getRemoteChatSessionDataClass(), BukkitConverters.getWrappedRemoteChatSessionDataConverter()).read(0)
);
}
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
return new PlayerInfoData(modifier.<UUID>withType(UUID.class).read(0),
latency,
modifier.<Boolean>withType(boolean.class).read(0),
gameMode,
gameProfile,
displayName,
modifier.withType(MinecraftReflection.getRemoteChatSessionDataClass(), BukkitConverters.getWrappedRemoteChatSessionDataConverter()).read(0)
);
}
WrappedProfileKeyData key = null;
if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
StructureModifier<WrappedProfileKeyData> keyData = modifier.withType(
MinecraftReflection.getProfilePublicKeyDataClass(), BukkitConverters.getWrappedPublicKeyDataConverter());
MinecraftReflection.getProfilePublicKeyDataClass(), BukkitConverters.getWrappedPublicKeyDataConverter());
key = keyData.read(0);
}
@@ -0,0 +1,45 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.utility.MinecraftReflection;
public class ResourceKey {
private static final AutoWrapper<ResourceKey> AUTO_WRAPPER = AutoWrapper.wrap(ResourceKey.class, MinecraftReflection.getResourceKey())
.field(0, MinecraftKey.getConverter())
.field(1, MinecraftKey.getConverter());
MinecraftKey registryName;
MinecraftKey location;
ResourceKey() {
}
public ResourceKey(MinecraftKey registryName, MinecraftKey location) {
this.registryName = registryName;
this.location = location;
}
public MinecraftKey getRegistryName() {
return registryName;
}
public MinecraftKey getLocation() {
return location;
}
public void setRegistryName(MinecraftKey registryName) {
this.registryName = registryName;
}
public void setLocation(MinecraftKey location) {
this.location = location;
}
public static ResourceKey fromGeneric(Object generic) {
return AUTO_WRAPPER.wrap(generic);
}
public Object toGeneric() {
return AUTO_WRAPPER.unwrap(this);
}
}
@@ -45,7 +45,7 @@ public abstract class WrappedBlockData extends AbstractWrapper implements Clonab
private static MethodAccessor TO_LEGACY_DATA;
private static MethodAccessor GET_BLOCK;
private static MethodAccessor BLOCK_FROM_MATERIAL;
private static MethodAccessor GET_BLOCK_DATA;
private static MethodAccessor DEFAULT_BLOCK_DATA;
private static MethodAccessor FROM_LEGACY_DATA;
private static MethodAccessor GET_HANDLE;
@@ -100,7 +100,7 @@ public abstract class WrappedBlockData extends AbstractWrapper implements Clonab
.parameterCount(0)
.returnTypeExact(IBLOCK_DATA)
.build();
GET_BLOCK_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
DEFAULT_BLOCK_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getCraftBukkitClass("block.data.CraftBlockData"));
contract = FuzzyMethodContract
@@ -131,7 +131,7 @@ public abstract class WrappedBlockData extends AbstractWrapper implements Clonab
@Override
public void setType(Material material) {
Object block = BLOCK_FROM_MATERIAL.invoke(null, material);
setHandle(GET_BLOCK_DATA.invoke(block));
setHandle(DEFAULT_BLOCK_DATA.invoke(block));
}
@Override
@@ -141,7 +141,7 @@ public abstract class WrappedBlockData extends AbstractWrapper implements Clonab
@Override
public void setTypeAndData(Material material, int data) {
setHandle(TO_LEGACY_DATA.invoke(null, material, (byte) data));
setHandle(FROM_LEGACY_DATA.invoke(null, material, (byte) data));
}
@Override
@@ -151,7 +151,7 @@ public abstract class WrappedBlockData extends AbstractWrapper implements Clonab
private static WrappedBlockData createNewData(Material material) {
Object block = BLOCK_FROM_MATERIAL.invoke(null, material);
return new NewBlockData(GET_BLOCK_DATA.invoke(block));
return new NewBlockData(DEFAULT_BLOCK_DATA.invoke(block));
}
private static WrappedBlockData createNewData(Material material, int data) {
@@ -1,8 +1,13 @@
package com.comphenix.protocol.wrappers;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.util.Optional;
import com.comphenix.protocol.wrappers.codecs.WrappedCodec;
import com.comphenix.protocol.wrappers.codecs.WrappedDynamicOps;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import org.bukkit.ChatColor;
import com.comphenix.protocol.reflect.FuzzyReflection;
@@ -35,41 +40,51 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
private static ConstructorAccessor CONSTRUCT_TEXT_COMPONENT = null;
static {
FuzzyReflection reflection = FuzzyReflection.fromClass(SERIALIZER, true);
private static WrappedCodec CODEC;
private static WrappedDynamicOps REGISTRY_JSON_OPS;
// Retrieve the correct methods
if (MinecraftVersion.v1_20_5.atOrAbove()) {
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(reflection.getMethod(FuzzyMethodContract.newBuilder()
static {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(SERIALIZER, true);
if (MinecraftVersion.v1_21_6.atOrAbove()) {
Field codecField = fuzzy.getFieldByType("CODEC", MinecraftReflection.getCodecClass());
CODEC = WrappedCodec.fromHandle(Accessors.getFieldAccessor(codecField).get(null));
REGISTRY_JSON_OPS = MinecraftRegistryAccess.createSerializationContext(WrappedDynamicOps.json(false));
} else if (MinecraftVersion.v1_20_5.atOrAbove()) {
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.returnTypeExact(String.class)
.parameterDerivedOf(COMPONENT)
.parameterDerivedOf(MinecraftReflection.getHolderLookupProviderClass())
.build()));
} else {
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(reflection.getMethodByReturnTypeAndParameters("serialize", /* a */
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters("serialize", /* a */
String.class, new Class<?>[] { COMPONENT }));
}
GSON = Accessors.getFieldAccessor(reflection.getFieldByType("gson", GSON_CLASS)).get(null);
if (!MinecraftVersion.v1_20_4.atOrAbove()) {
GSON = Accessors.getFieldAccessor(fuzzy.getFieldByType("gson", GSON_CLASS)).get(null);
}
if (MinecraftVersion.v1_20_5.atOrAbove()) {
DESERIALIZE = Accessors.getMethodAccessor(reflection.getMethod(FuzzyMethodContract.newBuilder()
.returnDerivedOf(COMPONENT)
.parameterExactType(String.class)
.parameterDerivedOf(MinecraftReflection.getHolderLookupProviderClass())
.build()));
} else if (MinecraftVersion.v1_20_4.atOrAbove()) {
DESERIALIZE = Accessors.getMethodAccessor(reflection
.getMethodByReturnTypeAndParameters("fromJson", MUTABLE_COMPONENT_CLASS.get(), new Class[] { String.class }));
} else {
try {
DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(MinecraftReflection.getChatDeserializer(), true)
.getMethodByReturnTypeAndParameters("deserialize", Object.class, new Class<?>[] { GSON_CLASS, String.class, Class.class, boolean.class }));
} catch (IllegalArgumentException ex) {
// We'll handle it in the ComponentParser
DESERIALIZE = null;
}
}
if (!MinecraftVersion.v1_21_6.atOrAbove()) {
if (MinecraftVersion.v1_20_5.atOrAbove()) {
DESERIALIZE = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.returnDerivedOf(COMPONENT)
.parameterExactType(String.class)
.parameterDerivedOf(MinecraftReflection.getHolderLookupProviderClass())
.build()));
} else if (MinecraftVersion.v1_20_4.atOrAbove()) {
DESERIALIZE = Accessors.getMethodAccessor(fuzzy
.getMethodByReturnTypeAndParameters("fromJson", MUTABLE_COMPONENT_CLASS.get(), new Class[]{String.class}));
} else {
try {
DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(MinecraftReflection.getChatDeserializer(), true)
.getMethodByReturnTypeAndParameters("deserialize", Object.class, new Class<?>[]{GSON_CLASS, String.class, Class.class, boolean.class}));
} catch (IllegalArgumentException ex) {
// We'll handle it in the ComponentParser
DESERIALIZE = null;
}
}
}
// Get a component from a standard Minecraft message
CONSTRUCT_COMPONENT = Accessors.getMethodAccessor(MinecraftReflection.getCraftChatMessage(), "fromString", String.class, boolean.class);
@@ -83,6 +98,11 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
}
private static Object serialize(Object handle) {
if (CODEC != null) {
Object jobj = CODEC.encode(handle, REGISTRY_JSON_OPS).getOrThrow(JsonParseException::new);
return jobj.toString();
}
if (MinecraftVersion.v1_20_5.atOrAbove()) {
return SERIALIZE_COMPONENT.invoke(null, handle, MinecraftRegistryAccess.get());
}
@@ -91,6 +111,10 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
}
private static Object deserialize(String json) {
if (CODEC != null) {
return CODEC.parse(JsonParser.parseString(json), REGISTRY_JSON_OPS).getOrThrow(JsonParseException::new);
}
if (MinecraftVersion.v1_20_5.atOrAbove()) {
return DESERIALIZE.invoke(null, json, MinecraftRegistryAccess.get());
}
@@ -0,0 +1,181 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.RecordComponent;
import java.util.Optional;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.cloning.AggregateCloner;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Represents a single data component in Minecraft, which is a key-value pair
* used to store metadata about game objects such as items, mobs, and block entities.
* <p>
* This class provides methods to create, access, and manipulate data components,
* as well as to convert them to and from their underlying NMS handles.
*
* @see WrappedDataComponentHolder
*/
public final class WrappedDataComponent {
private static final Optional<Class<?>> HANDLE_TYPE = MinecraftReflection.getTypedDataComponentClass();
private static MethodAccessor TYPE_ACCESSOR;
private static MethodAccessor VALUE_ACCESSOR;
private static ConstructorAccessor CTOR_ACCESSOR;
private final Object keyHandle;
private final String key;
private Object value;
static {
initialize();
}
private static void initialize() {
if (HANDLE_TYPE.isEmpty()) {
return;
}
Class<?> clazz = HANDLE_TYPE.get();
RecordComponent typeComponent = clazz.getRecordComponents()[0];
TYPE_ACCESSOR = Accessors.getMethodAccessor(typeComponent.getAccessor());
RecordComponent valueComponent = clazz.getRecordComponents()[1];
VALUE_ACCESSOR = Accessors.getMethodAccessor(valueComponent.getAccessor());
CTOR_ACCESSOR = Accessors.getConstructorAccessor(clazz, typeComponent.getType(), valueComponent.getType());
}
WrappedDataComponent(Object keyHandle, String key, Object value) {
this.keyHandle = keyHandle;
this.key = key;
this.value = value;
}
/**
* @return the key of this data component, e.g. "minecraft:custom_data".
*/
public String getKey() {
return key;
}
/**
* Updates the in-memory value of this data component.
* <br>
* <b>NOTE:</b> this does <b>NOT</b> update the underlying handle
* @param value new NMS value
*/
public void setValue(Object value) {
this.value = value;
}
/**
* @return the current in-memory value of this data component
* @param <T> desired return type
*/
@SuppressWarnings("unchecked")
public <T> T getValue() {
return (T) value;
}
/**
* Creates a new WrappedDataComponent from an existing NMS handle.
* @param handle NMS handle
* @return new WrappedDataComponent instance
*/
public static WrappedDataComponent fromHandle(Object handle) {
if (TYPE_ACCESSOR == null || VALUE_ACCESSOR == null) {
throw new IllegalStateException("Unable to access TypedDataComponent");
}
Object typeHandle = TYPE_ACCESSOR.invoke(handle);
Object value = VALUE_ACCESSOR.invoke(handle);
WrappedRegistry componentTypeRegistry = WrappedRegistry.getDataComponentTypeRegistry();
String key = componentTypeRegistry.getKey(typeHandle).getFullKey();
return new WrappedDataComponent(typeHandle, key, value);
}
/**
* Creates a new NMS handle for this data component using the current value.
* @return new NMS handle
*/
public Object getHandle() {
if (CTOR_ACCESSOR == null) {
throw new IllegalStateException("Unable to access TypedDataComponent constructor");
}
return CTOR_ACCESSOR.invoke(keyHandle, value);
}
/**
* Creates a new WrappedDataComponent with the specified key and no value.
* @param key the key of the data component, e.g. "minecraft:custom_data".
* @return new WrappedDataComponent instance
*/
public static WrappedDataComponent create(String key) {
return create(key, null);
}
/**
* Creates a new WrappedDataComponent with the specified key and value.
* @param key the key of the data component, e.g. "minecraft:custom_data".
* @param value the NMS value of the data component, e.g. an ItemStack or Integer.
* @return new WrappedDataComponent instance
*/
public static WrappedDataComponent create(String key, Object value) {
WrappedRegistry componentTypeRegistry = WrappedRegistry.getDataComponentTypeRegistry();
Object keyHandle = componentTypeRegistry.get(key);
if (keyHandle == null) {
throw new IllegalArgumentException("Invalid key: " + key);
}
return new WrappedDataComponent(keyHandle, key, value);
}
/**
* Creates a shallow clone of this WrappedDataComponent, which means it copies the key and value references,
* but does not clone the value itself. This should cover most cases where the value is immutable
* (e.g. strings, numbers, enums).
* @return a new WrappedDataComponent instance with the same key and value references
*/
public WrappedDataComponent shallowClone() {
return new WrappedDataComponent(keyHandle, key, value);
}
/**
* Creates a deep clone of this WrappedDataComponent, which means it clones the value as well.
* This is useful when the value is mutable (e.g. books, NBT data) and you want to ensure that
* changes to the cloned component do not affect the original component.
* @return a new WrappedDataComponent instance with a cloned value
*/
public WrappedDataComponent deepClone() {
Object clonedValue = AggregateCloner.DEFAULT.clone(value);
return new WrappedDataComponent(keyHandle, key, clonedValue);
}
@Override
public String toString() {
return "WrappedDataComponent[" + key + "=>" + value + "]";
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof WrappedDataComponent other)) return false;
return key.equals(other.key) && value.equals(other.value);
}
@Override
public int hashCode() {
int result = key.hashCode();
result = 31 * result + (value != null ? value.hashCode() : 0);
return result;
}
}
@@ -0,0 +1,77 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import org.bukkit.inventory.ItemStack;
/**
* Provides access to a DataComponentHolder, which is an object that can hold
* various (meta)data components
*/
public final class WrappedDataComponentHolder extends AbstractWrapper {
private static MethodAccessor GET_COMPONENTS;
private static MethodAccessor GET_COMPONENT;
private static Optional<Class<?>> DATA_COMPONENT_MAP_CLASS = MinecraftReflection.getDataComponentMapClass();
private static Optional<Class<?>> DATA_COMPONENT_HOLDER_CLASS = MinecraftReflection.getDataComponentHolderClass();
static {
initialize();
}
private static void initialize() {
if (DATA_COMPONENT_MAP_CLASS.isEmpty() || DATA_COMPONENT_HOLDER_CLASS.isEmpty()) {
return;
}
Class<?> mapClass = DATA_COMPONENT_MAP_CLASS.get();
Class<?> holderClass = DATA_COMPONENT_HOLDER_CLASS.get();
FuzzyReflection fuzzy = FuzzyReflection.fromClass(holderClass, true);
GET_COMPONENTS = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterCount(0)
.returnTypeExact(mapClass)
.build()));
GET_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterCount(1)
//.requireModifier(Modifier.PUBLIC)
.banModifier(Modifier.STATIC)
.parameterExactType(MinecraftReflection.getMinecraftClass("core.component.DataComponentType"))
.build()));
}
public WrappedDataComponentHolder(Object handle) {
super(DATA_COMPONENT_HOLDER_CLASS.orElseThrow(() -> new IllegalStateException("DataComponentHolder class is not available in this version")));
setHandle(handle);
}
public Map<String, WrappedDataComponent> getAllComponents() {
Iterable<?> components = (Iterable<?>) GET_COMPONENTS.invoke(handle);
Map<String, WrappedDataComponent> wrapped = new HashMap<>();
for (Object componentHandle : components) {
WrappedDataComponent component = WrappedDataComponent.fromHandle(componentHandle);
wrapped.put(component.getKey(), component);
}
return wrapped;
}
public WrappedDataComponent getComponent(String key) {
Object keyHandle = WrappedRegistry.getDataComponentTypeRegistry().get(key);
Object valueHandle = GET_COMPONENT.invoke(handle, keyHandle);
return new WrappedDataComponent(keyHandle, key, valueHandle);
}
public static WrappedDataComponentHolder fromItemStack(ItemStack itemStack) {
Object nmsStack = MinecraftReflection.getMinecraftItemStack(itemStack);
return new WrappedDataComponentHolder(nmsStack);
}
}
@@ -24,6 +24,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
@@ -35,13 +36,12 @@ import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.utility.Validate;
import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
import com.comphenix.protocol.wrappers.EnumWrappers.EntityPose;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.lang.Validate;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
@@ -66,7 +66,7 @@ public class WrappedDataWatcher implements IDataWatcher {
* Constructs a new DataWatcher wrapper around a NMS handle. The resulting
* DataWatcher will likely have existing values that can be removed with
* {@link #clear()}.
*
*
* @param handle DataWatcher handle
*/
public WrappedDataWatcher(Object handle) {
@@ -90,7 +90,7 @@ public class WrappedDataWatcher implements IDataWatcher {
* Constructs a new DataWatcher using a real entity. The resulting
* DataWatcher will not have any keys or values and new ones will have to
* be added using watcher objects.
*
*
* @param entity The entity
*/
public WrappedDataWatcher(Entity entity) {
@@ -102,7 +102,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Constructs a new DataWatcher using a fake egg entity and a given
* list of watchable objects.
*
*
* @param objects The list of objects
*/
public WrappedDataWatcher(List<WrappedWatchableObject> objects) {
@@ -152,7 +152,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Gets the item at a given index.
*
*
* @param index Index to get
* @return The watchable object, or null if none exists
*/
@@ -170,7 +170,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Removes the item at a given index.
*
*
* @param index Index to remove
* @return The previous value, or null if none existed
*/
@@ -180,7 +180,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Whether or not this DataWatcher has an object at a given index.
*
*
* @param index Index to check for
* @return True if it does, false if not
*/
@@ -209,7 +209,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Get a watched byte.
*
*
* @param index - index of the watched byte.
* @return The watched byte, or NULL if this value doesn't exist.
*/
@@ -225,7 +225,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Get a watched short.
*
*
* @param index - index of the watched short.
* @return The watched short, or NULL if this value doesn't exist.
*/
@@ -237,7 +237,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Get a watched integer.
*
*
* @param index - index of the watched integer.
* @return The watched integer, or NULL if this value doesn't exist.
*/
@@ -263,7 +263,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Get a watched float.
*
*
* @param index - index of the watched float.
* @return The watched float, or NULL if this value doesn't exist.
*/
@@ -279,7 +279,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Get a watched string.
*
*
* @param index - index of the watched string.
* @return The watched string, or NULL if this value doesn't exist.
*/
@@ -315,7 +315,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Get a watched string.
*
*
* @param index - index of the watched string.
* @return The watched string, or NULL if this value doesn't exist.
*/
@@ -477,7 +477,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Retrieve a watchable object by index.
*
*
* @param index Index of the object to retrieve.
* @return The watched object or null if it doesn't exist.
*/
@@ -487,7 +487,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Retrieve a watchable object by watcher object.
*
*
* @param object The watcher object
* @return The watched object or null if it doesn't exist.
*/
@@ -500,11 +500,11 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Sets the DataWatcher Item at a given index to a new value. In 1.9 and up,
* you cannot register objects without a watcher object.
*
*
* @param index Index of the object to set
* @param value New value
* @param update Whether or not to inform the client
*
*
* @see WrappedDataWatcher#setObject(WrappedDataWatcherObject, Object, boolean)
* @throws IllegalArgumentException in 1.9 and up if there isn't already an
* object at this index
@@ -526,12 +526,12 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Sets the DataWatcher Item at a given index to a new value.
*
*
* @param index Index of the object to set
* @param serializer Serializer from {@link Registry#get(Class)}
* @param value New value
* @param update Whether or not to inform the client
*
*
* @see WrappedDataWatcher#setObject(WrappedDataWatcherObject, Object)
*/
public void setObject(int index, Serializer serializer, Object value, boolean update) {
@@ -547,11 +547,11 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Sets the DataWatcher Item at a given index to a new value.
*
*
* @param index Index of the object to set
* @param value New value
* @param update Whether or not to inform the client
*
*
* @see WrappedDataWatcher#setObject(int, Object, boolean)
*/
public void setObject(int index, WrappedWatchableObject value, boolean update) {
@@ -567,11 +567,11 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Sets the DataWatcher Item associated with a given watcher object to a new value.
*
*
* @param object Associated watcher object
* @param value Wrapped value
* @param update Whether or not to inform the client
*
*
* @see #setObject(WrappedDataWatcherObject, Object)
*/
public void setObject(WrappedDataWatcherObject object, WrappedWatchableObject value, boolean update) {
@@ -589,10 +589,10 @@ public class WrappedDataWatcher implements IDataWatcher {
* Sets the DataWatcher Item associated with a given watcher object to a
* new value. If there is not already an object at this index, the
* specified watcher object must have a serializer.
*
*
* @param object Associated watcher object
* @param value New value
*
*
* @throws IllegalArgumentException If the watcher object is null or must
* have a serializer and does not have one.
*/
@@ -611,7 +611,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Clone the content of the current DataWatcher.
*
*
* @return A cloned data watcher.
*/
public WrappedDataWatcher deepClone() {
@@ -628,7 +628,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Retrieve the data watcher associated with an entity.
*
*
* @param entity - the entity to read from.
* @return Associated data watcher.
*/
@@ -682,7 +682,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Retrieves the type ID associated with a given class. No longer supported
* in 1.9 and up due to the removal of type IDs.
*
*
* @param clazz Class to find ID for
* @return The ID, or null if not found
*/
@@ -693,7 +693,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Retrieves the class associated with a given type ID. No longer
* supported in 1.9 and up due to the removal of type IDs.
*
*
* @param typeID ID to find Class for
* @return The Class, or null if not found
*/
@@ -729,7 +729,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Represents a DataWatcherObject in 1.9. In order to register an object,
* the serializer must be specified.
*
*
* @author dmulloy2
*/
public static class WrappedDataWatcherObject {
@@ -745,7 +745,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Creates a new watcher object from a NMS handle.
*
*
* @param handle The handle
*/
public WrappedDataWatcherObject(Object handle) {
@@ -755,7 +755,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Creates a new watcher object from an index and serializer.
*
*
* @param index Index
* @param serializer Serializer, see {@link Registry}
*/
@@ -795,7 +795,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Gets this watcher object's index.
*
*
* @return The index
*/
public int getIndex() {
@@ -805,7 +805,7 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Gets this watcher object's serializer. Will return null if the
* serializer was never specified.
*
*
* @return The serializer, or null
*/
public Serializer getSerializer() {
@@ -911,23 +911,36 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Represents a DataWatcherSerializer in 1.9. If a Serializer is optional,
* values must be wrapped in a {@link Optional}.
*
*
* @author dmulloy2
*/
public static class Serializer extends AbstractWrapper {
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getDataWatcherSerializerClass();
private final Class<?> type;
private final Type type;
private final boolean optional;
/**
* Constructs a new Serializer
*
*
* @param type Type it serializes
* @param handle NMS handle
* @param optional Whether or not it's {@link Optional}
* @deprecated Use {@link Serializer#Serializer(Type, Object, boolean)} instead.
*/
@Deprecated(forRemoval = true)
public Serializer(Class<?> type, Object handle, boolean optional) {
this((Type) type, handle, optional);
}
/**
* Constructs a new Serializer
*
* @param type Type it serializes
* @param handle NMS handle
* @param optional Whether or not it's {@link Optional}
*/
public Serializer(Class<?> type, Object handle, boolean optional) {
public Serializer(Type type, Object handle, boolean optional) {
super(HANDLE_TYPE);
this.type = type;
this.optional = optional;
@@ -937,17 +950,32 @@ public class WrappedDataWatcher implements IDataWatcher {
/**
* Gets the type this serializer serializes.
*
*
* @return The type
* @deprecated use {@link #getGenericType()} instead.
*/
@Deprecated(forRemoval = true)
public Class<?> getType() {
return type;
if (this.type instanceof Class<?> c) {
return c;
}
throw new IllegalStateException("Serializer does not serialize a class");
}
/**
* Gets the type this serializer serializes.
*
* @return The type.
*/
public Type getGenericType() {
return this.type;
}
/**
* Whether or not this serializer is optional, that is whether or not
* the return type is wrapped in a {@link Optional}.
*
*
* @return True if it is, false if not
*/
public boolean isOptional() {
@@ -986,11 +1014,19 @@ public class WrappedDataWatcher implements IDataWatcher {
public static class Registry {
private static boolean INITIALIZED = false;
private static Map<Class<?>, Serializer> RAW_REGISTRY = null;
private static Map<Class<?>, Serializer> OPTIONAL_REGISTRY = null;
private static Map<Type, Serializer> RAW_REGISTRY = null;
private static Map<Type, Serializer> OPTIONAL_REGISTRY = null;
/**
* Gets the first serializer associated with a given class.
* @deprecated use {@link #get(Type)} instead.
*/
@Deprecated(forRemoval = true)
public static Serializer get(Class<?> clazz) {
return get((Type) clazz);
}
/**
* Gets the first serializer associated with a given type.
*
* <p><b>Note</b>: If {@link Serializer#isOptional() the serializer is optional},
* values <i>must</i> be wrapped in an {@link Optional}.</p>
@@ -998,39 +1034,46 @@ public class WrappedDataWatcher implements IDataWatcher {
* <p>If there are multiple serializers for a given class (i.e. BlockPosition),
* you should use {@link #get(Class, boolean)} for more precision.</p>
*
* @param clazz Class to find serializer for
* @param type Type to find serializer for
* @return The serializer, or null if none exists
*/
public static Serializer get(Class<?> clazz) {
Validate.notNull(clazz,"Class cannot be null!");
public static Serializer get(Type type) {
Validate.notNull(type,"Type cannot be null!");
initialize();
Serializer serializer = RAW_REGISTRY.getOrDefault(clazz,
OPTIONAL_REGISTRY.getOrDefault(clazz, null));
Serializer serializer = RAW_REGISTRY.getOrDefault(type, OPTIONAL_REGISTRY.get(type));
if (serializer == null) {
throw new IllegalArgumentException("No serializer found for " + clazz);
throw new IllegalArgumentException("No serializer found for " + type);
}
return serializer;
}
/**
* Gets the first serializer associated with a given class and optional state.
* @deprecated use {@link #get(Type, boolean)} instead.
*/
@Deprecated(forRemoval = true)
public static Serializer get(Class<?> clazz, boolean optional) {
return get((Type) clazz, optional);
}
/**
* Gets the first serializer associated with a given type and optional state.
* <br/>
* <b>Note</b>: If the serializer is optional, values <i>must</i> be
* wrapped in an {@link Optional}
*
* @param clazz Class to find serializer for
* @param type Type to find serializer for
* @param optional Optional state
* @return The serializer, or null if none exists
*/
public static Serializer get(Class<?> clazz, boolean optional) {
Validate.notNull(clazz, "Class cannot be null!");
public static Serializer get(Type type, boolean optional) {
Validate.notNull(type, "Type cannot be null!");
initialize();
Serializer serializer = optional ? OPTIONAL_REGISTRY.get(clazz) : RAW_REGISTRY.get(clazz);
Serializer serializer = optional ? OPTIONAL_REGISTRY.get(type) : RAW_REGISTRY.get(type);
if (serializer == null) {
throw new IllegalArgumentException("No serializer found for " + (optional ? "Optional<" + clazz + ">" : clazz));
throw new IllegalArgumentException("No serializer found for " + (optional ? "Optional<" + type + ">" : type));
}
return serializer;
@@ -1067,52 +1110,41 @@ public class WrappedDataWatcher implements IDataWatcher {
return;
}
Map<Class<?>, Serializer> rawRegistry = new HashMap<>();
Map<Class<?>, Serializer> optionalRegistry = new HashMap<>();
Map<Type, Serializer> rawRegistry = new HashMap<>();
Map<Type, Serializer> optionalRegistry = new HashMap<>();
List<Field> candidates = FuzzyReflection.fromClass(MinecraftReflection.getDataWatcherRegistryClass(), true)
.getFieldListByType(MinecraftReflection.getDataWatcherSerializerClass());
for (Field candidate : candidates) {
Type generic = candidate.getGenericType();
if (generic instanceof ParameterizedType) {
ParameterizedType type = (ParameterizedType) generic;
Type[] args = type.getActualTypeArguments();
Type arg = args[0];
Class<?> innerClass;
boolean optional = false;
if (arg instanceof Class<?>) {
innerClass = (Class<?>) arg;
} else if (arg instanceof ParameterizedType) {
innerClass = (Class<?>) ((ParameterizedType) arg).getActualTypeArguments()[0];
optional = true;
} else {
throw new IllegalStateException("Failed to find inner class of field " + candidate);
}
if (candidate.getGenericType() instanceof ParameterizedType cpt) {
Object serializer;
try {
serializer = candidate.get(null);
Objects.requireNonNull(serializer, "Failed to read serializer: " + candidate.getName());
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Failed to read field " + candidate);
}
if (serializer == null) {
throw new RuntimeException("Failed to read serializer: " + candidate.getName());
// all types are wrapped in EntityDataSerializer<?>
Type serializingType = cpt.getActualTypeArguments()[0];
if (serializingType instanceof ParameterizedType spt) {
Class<?> root = (Class<?>) spt.getRawType();
if (root == Optional.class) {
// special case: EntityDataSerializer<Optional<?>>
Type actualType = spt.getActualTypeArguments()[0];
Serializer wrappedSerializer = new Serializer(actualType, serializer, true);
optionalRegistry.put(actualType, wrappedSerializer);
continue;
}
}
if (optional) {
optionalRegistry.put(innerClass, new Serializer(innerClass, serializer, true));
} else {
rawRegistry.put(innerClass, new Serializer(innerClass, serializer, false));
}
Serializer wrappedSerializer = new Serializer(serializingType, serializer, false);
rawRegistry.put(serializingType, wrappedSerializer);
}
}
RAW_REGISTRY = ImmutableMap.copyOf(rawRegistry);
OPTIONAL_REGISTRY = ImmutableMap.copyOf(optionalRegistry);
RAW_REGISTRY = Map.copyOf(rawRegistry);
OPTIONAL_REGISTRY = Map.copyOf(optionalRegistry);
}
// ---- Helper methods
@@ -4,7 +4,9 @@ import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import java.util.Map;
import com.comphenix.protocol.reflect.EquivalentConverter;
import org.jetbrains.annotations.Nullable;
import com.comphenix.protocol.injector.StructureCache;
@@ -41,6 +43,7 @@ public final class WrappedLevelChunkData {
private static final FieldAccessor BLOCK_ENTITIES_DATA_ACCESSOR;
private static final FieldAccessor HEIGHTMAPS_ACCESSOR;
private static final EquivalentConverter<Map<EnumWrappers.HeightmapType, long[]>> HEIGHTMAPS_CONVERTER;
private static final FieldAccessor BUFFER_ACCESSOR;
static {
@@ -54,9 +57,17 @@ public final class WrappedLevelChunkData {
BLOCK_ENTITIES_DATA_ACCESSOR = Accessors.getFieldAccessor(reflection.getField(FuzzyFieldContract.newBuilder()
.typeExact(List.class)
.build()));
HEIGHTMAPS_ACCESSOR = Accessors.getFieldAccessor(reflection.getField(FuzzyFieldContract.newBuilder()
.typeExact(MinecraftReflection.getNBTCompoundClass())
.build()));
if (MinecraftVersion.v1_21_5.atOrAbove()) {
HEIGHTMAPS_ACCESSOR = Accessors.getFieldAccessor(reflection.getField(FuzzyFieldContract.newBuilder()
.typeExact(Map.class)
.build()));
HEIGHTMAPS_CONVERTER = BukkitConverters.getMapConverter(EnumWrappers.getHeightmapTypeConverter(), Converters.passthrough(long[].class));
} else {
HEIGHTMAPS_ACCESSOR = Accessors.getFieldAccessor(reflection.getField(FuzzyFieldContract.newBuilder()
.typeExact(MinecraftReflection.getNBTCompoundClass())
.build()));
HEIGHTMAPS_CONVERTER = null;
}
BUFFER_ACCESSOR = Accessors.getFieldAccessor(reflection.getField(FuzzyFieldContract.newBuilder().typeExact(byte[].class).build()));
}
@@ -68,22 +79,50 @@ public final class WrappedLevelChunkData {
/**
* The heightmap of this chunk.
* <p>
* Removed in Minecraft 1.21.5.
*
* @return an NBT-Tag
* @deprecated Use {@link ChunkData#getHeightmaps()} instead.
*/
@Deprecated
public NbtCompound getHeightmapsTag() {
return NbtFactory.fromNMSCompound(HEIGHTMAPS_ACCESSOR.get(handle));
}
/**
* Sets the heightmap tag of this chunk.
* <p>
* Removed in Minecraft 1.21.5.
*
* @param heightmapsTag the new heightmaps tag.
* @deprecated Use {@link ChunkData#setHeightmaps(Map)} instead.
*/
@Deprecated
public void setHeightmapsTag(NbtCompound heightmapsTag) {
HEIGHTMAPS_ACCESSOR.set(handle, NbtFactory.fromBase(heightmapsTag).getHandle());
}
/**
* The heightmap of this chunk.
*
* @return a map containing the heightmaps
*/
public Map<EnumWrappers.HeightmapType, long[]> getHeightmaps() {
return HEIGHTMAPS_CONVERTER.getSpecific(HEIGHTMAPS_ACCESSOR.get(handle));
}
/**
* Sets the heightmap tag of this chunk.
* <p>
* Removed in Minecraft 1.21.5.
*
* @param heightmaps the new heightmaps.
*/
public void setHeightmaps(Map<EnumWrappers.HeightmapType, long[]> heightmaps) {
HEIGHTMAPS_ACCESSOR.set(handle, HEIGHTMAPS_CONVERTER.getGeneric(heightmaps));
}
/**
* The actual structural data of this chunk as bytes.
*
@@ -130,12 +169,16 @@ public final class WrappedLevelChunkData {
/**
* Creates a new wrapper using predefined values.
* <p>
* Removed in Minecraft 1.21.5.
*
* @param heightmapsTag the heightmaps tag
* @param buffer the buffer
* @param blockEntityInfo a list of wrapped block entities
* @return a newly created wrapper
* @deprecated Use {@link ChunkData#fromValues(Map, byte[], List)} instead.
*/
@Deprecated
public static ChunkData fromValues(NbtCompound heightmapsTag, byte[] buffer, List<BlockEntityInfo> blockEntityInfo) {
ChunkData data = new ChunkData(LEVEL_CHUNK_PACKET_DATA_CONSTRUCTOR.invoke(StructureCache.newNullDataSerializer(), 0, 0));
@@ -145,6 +188,24 @@ public final class WrappedLevelChunkData {
return new ChunkData(data);
}
/**
* Creates a new wrapper using predefined values.
*
* @param heightmaps the heightmaps
* @param buffer the buffer
* @param blockEntityInfo a list of wrapped block entities
* @return a newly created wrapper
*/
public static ChunkData fromValues(Map<EnumWrappers.HeightmapType, long[]> heightmaps, byte[] buffer, List<BlockEntityInfo> blockEntityInfo) {
ChunkData data = new ChunkData(LEVEL_CHUNK_PACKET_DATA_CONSTRUCTOR.invoke(StructureCache.newNullDataSerializer(), 0, 0));
data.setHeightmaps(heightmaps);
data.setBuffer(buffer);
data.setBlockEntityInfo(blockEntityInfo);
return new ChunkData(data);
}
}
/**
@@ -183,59 +183,80 @@ public class WrappedParticle<T> {
}
private static Object getRedstone(Object handle) {
int r, g, b;
float size;
Color color;
float scale;
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
StructureModifier<Object> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle);
StructureModifier<Object> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle);
if (MinecraftVersion.v1_21_2.atOrAbove()) {
int rgb = (int) modifier.withType(int.class).read(0);
color = Color.fromRGB(rgb);
scale = (float) modifier.withType(float.class).read(0);
} else if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
org.joml.Vector3f rgb = (org.joml.Vector3f) modifier.withType(org.joml.Vector3f.class).read(0);
r = (int) (rgb.x() * 255);
g = (int) (rgb.y() * 255);
b = (int) (rgb.z() * 255);
size = (float) modifier.withType(float.class).read(0);
int red = (int) (rgb.x() * 255);
int green = (int) (rgb.y() * 255);
int blue = (int) (rgb.z() * 255);
color = Color.fromRGB(red, green, blue);
scale = (float) modifier.withType(float.class).read(0);
} else if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
if (VECTOR_3FA == null) {
VECTOR_3FA = MinecraftReflection.getLibraryClass("com.mojang.math.Vector3fa");
}
StructureModifier<Object> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle);
Object rgb = modifier.withType(VECTOR_3FA).read(0);
StructureModifier<Object> rgbModifier = new StructureModifier<>(VECTOR_3FA).withTarget(rgb);
r = (int) (rgbModifier.<Float>withType(float.class).read(0) * 255);
g = (int) (rgbModifier.<Float>withType(float.class).read(1) * 255);
b = (int) (rgbModifier.<Float>withType(float.class).read(2) * 255);
size = (float) modifier.withType(float.class).read(0);
int red = (int) (rgbModifier.<Float>withType(float.class).read(0) * 255);
int green = (int) (rgbModifier.<Float>withType(float.class).read(1) * 255);
int blue = (int) (rgbModifier.<Float>withType(float.class).read(2) * 255);
color = Color.fromRGB(red, green, blue);
scale = (float) modifier.withType(float.class).read(0);
} else {
StructureModifier<Float> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle).withType(float.class);
r = (int) (modifier.read(0) * 255);
g = (int) (modifier.read(1) * 255);
b = (int) (modifier.read(2) * 255);
size = modifier.read(3);
StructureModifier<Float> floatModifier = modifier.withType(float.class);
int red = (int) (floatModifier.read(0) * 255);
int green = (int) (floatModifier.read(1) * 255);
int blue = (int) (floatModifier.read(2) * 255);
color = Color.fromRGB(red, green, blue);
scale = floatModifier.read(3);
}
return new Particle.DustOptions(Color.fromRGB(r, g, b), size);
return new Particle.DustOptions(color, scale);
}
private static Object getDustTransition(Object handle) {
int fromR, fromG, fromB, toR, toG, toB;
float size;
Color fromColor, toColor;
float scale;
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
if (MinecraftVersion.v1_21_2.atOrAbove()) {
StructureModifier<Object> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle);
int fromRgb = (int) modifier.withType(int.class).read(0);
fromColor = Color.fromRGB(fromRgb);
int toRgb = (int) modifier.withType(int.class).read(1);
toColor = Color.fromRGB(toRgb);
scale = (float) modifier.withType(float.class).read(0);
} else if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
StructureModifier<Object> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle);
org.joml.Vector3f toRGB = (org.joml.Vector3f) modifier.withType(org.joml.Vector3f.class).read(1);
org.joml.Vector3f fromRGB = (org.joml.Vector3f) modifier.withType(org.joml.Vector3f.class).read(0);
size = (float) modifier.withType(float.class).read(0);
scale = (float) modifier.withType(float.class).read(0);
fromR = (int) (fromRGB.x() * 255);
fromG = (int) (fromRGB.y() * 255);
fromB = (int) (fromRGB.z() * 255);
int fromR = (int) (fromRGB.x() * 255);
int fromG = (int) (fromRGB.y() * 255);
int fromB = (int) (fromRGB.z() * 255);
fromColor = Color.fromRGB(fromR, fromG, fromB);
toR = (int) (toRGB.x() * 255);
toG = (int) (toRGB.y() * 255);
toB = (int) (toRGB.z() * 255);
int toR = (int) (toRGB.x() * 255);
int toG = (int) (toRGB.y() * 255);
int toB = (int) (toRGB.z() * 255);
toColor = Color.fromRGB(toR, toG, toB);
} else if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
if (VECTOR_3FA == null) {
VECTOR_3FA = MinecraftReflection.getLibraryClass("com.mojang.math.Vector3fa");
@@ -245,37 +266,42 @@ public class WrappedParticle<T> {
Object toRGB = modifier.withType(VECTOR_3FA).read(0);
Object fromRGB = modifier.withType(VECTOR_3FA).read(1);
size = (float) modifier.withType(float.class).read(0);
scale = (float) modifier.withType(float.class).read(0);
StructureModifier<Object> rgbModifier = new StructureModifier<>(VECTOR_3FA).withTarget(fromRGB);
StructureModifier<Object> rgbModifier2 = new StructureModifier<>(VECTOR_3FA).withTarget(toRGB);
fromR = (int) (rgbModifier.<Float>withType(float.class).read(0) * 255);
fromG = (int) (rgbModifier.<Float>withType(float.class).read(1) * 255);
fromB = (int) (rgbModifier.<Float>withType(float.class).read(2) * 255);
int fromR = (int) (rgbModifier.<Float>withType(float.class).read(0) * 255);
int fromG = (int) (rgbModifier.<Float>withType(float.class).read(1) * 255);
int fromB = (int) (rgbModifier.<Float>withType(float.class).read(2) * 255);
fromColor = Color.fromRGB(fromR, fromG, fromB);
toR = (int) (rgbModifier2.<Float>withType(float.class).read(0) * 255);
toG = (int) (rgbModifier2.<Float>withType(float.class).read(1) * 255);
toB = (int) (rgbModifier2.<Float>withType(float.class).read(2) * 255);
int toR = (int) (rgbModifier2.<Float>withType(float.class).read(0) * 255);
int toG = (int) (rgbModifier2.<Float>withType(float.class).read(1) * 255);
int toB = (int) (rgbModifier2.<Float>withType(float.class).read(2) * 255);
toColor = Color.fromRGB(toR, toG, toB);
} else {
StructureModifier<Float> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle).withType(float.class);
toR = (int) (modifier.read(0) * 255);
toG = (int) (modifier.read(1) * 255);
toB = (int) (modifier.read(2) * 255);
size = modifier.read(3);
fromR = (int) (modifier.read(4) * 255);
fromG = (int) (modifier.read(5) * 255);
fromB = (int) (modifier.read(6) * 255);
int toR = (int) (modifier.read(0) * 255);
int toG = (int) (modifier.read(1) * 255);
int toB = (int) (modifier.read(2) * 255);
toColor = Color.fromRGB(toR, toG, toB);
scale = modifier.read(3);
int fromR = (int) (modifier.read(4) * 255);
int fromG = (int) (modifier.read(5) * 255);
int fromB = (int) (modifier.read(6) * 255);
fromColor = Color.fromRGB(fromR, fromG, fromB);
}
return new Particle.DustTransition(Color.fromRGB(fromR, fromG, fromB), Color.fromRGB(toR, toG, toB), size);
return new Particle.DustTransition(fromColor, toColor, scale);
}
public static <T> WrappedParticle<T> create(Particle particle, T data) {
ensureMethods();
Object bukkitData = data;
if (data instanceof WrappedBlockData) {
WrappedBlockData blockData = (WrappedBlockData) data;
if (data instanceof WrappedBlockData blockData) {
bukkitData = toCraftData.invoke(null, blockData.getHandle());
}
@@ -1,14 +1,5 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.collect.ImmutableMap;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
@@ -19,21 +10,79 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.collect.ImmutableMap;
public class WrappedRegistry {
// map of NMS class to registry instance
private static final Map<Class<?>, WrappedRegistry> REGISTRY;
private static final Map<Class<?>, WrappedRegistry> BY_CLASS;
private static final Map<String, WrappedRegistry> BY_KEY;
private static final MethodAccessor GET;
private static final MethodAccessor GET_ID;
private static final MethodAccessor GET_KEY;
private static final MethodAccessor GET_BY_KEY;
private static final MethodAccessor GET_ITEM_ID;
private static final MethodAccessor GET_ITEM_KEY;
private static final MethodAccessor GET_REGISTRY_KEY;
private static final MethodAccessor GET_HOLDER;
static {
Map<Class<?>, WrappedRegistry> regMap = new HashMap<>();
Class<?> iRegistry = MinecraftReflection.getIRegistry();
FuzzyReflection fuzzy = FuzzyReflection.fromClass(iRegistry, false);
GET_BY_KEY = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(1)
.returnTypeMatches(FuzzyMatchers.and(FuzzyMatchers.assignable(Object.class), FuzzyMatchers.except(Optional.class)))
.requireModifier(Modifier.ABSTRACT)
.parameterExactType(MinecraftReflection.getMinecraftKeyClass())
.build()));
GET_ITEM_ID = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(1)
.returnTypeExact(int.class)
.requireModifier(Modifier.ABSTRACT)
.parameterDerivedOf(Object.class)
.build()));
GET_ITEM_KEY = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(1)
.returnTypeExact(MinecraftReflection.getMinecraftKeyClass())
.build()));
GET_REGISTRY_KEY = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(0)
.returnTypeExact(MinecraftReflection.getResourceKey())
.build()));
MethodAccessor getHolder = null;
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
try {
getHolder = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(1)
.banModifier(Modifier.STATIC)
.returnTypeExact(MinecraftReflection.getHolderClass())
.requireModifier(Modifier.PUBLIC)
.build()));
} catch (IllegalArgumentException ignored) {
}
}
GET_HOLDER = getHolder;
Map<Class<?>, WrappedRegistry> byClass = new HashMap<>();
Map<String, WrappedRegistry> byKey = new HashMap<>();
// get the possible registry fields
Class<?> iRegistry = MinecraftReflection.getIRegistry();
Class<?> builtInRegistries = MinecraftReflection.getBuiltInRegistries();
Set<Field> registries = FuzzyReflection.combineArrays(
iRegistry == null ? null : iRegistry.getFields(),
@@ -43,95 +92,75 @@ public class WrappedRegistry {
for (Field field : registries) {
try {
// make sure it's actually a registry
if (iRegistry.isAssignableFrom(field.getType())) {
Type genType = field.getGenericType();
if (genType instanceof ParameterizedType) {
ParameterizedType par = (ParameterizedType) genType;
Type paramType = par.getActualTypeArguments()[0];
if (paramType instanceof Class) {
// for example Registry<Item>
regMap.put((Class<?>) paramType, new WrappedRegistry(field.get(null)));
} else if (paramType instanceof ParameterizedType) {
// for example Registry<EntityType<?>>
par = (ParameterizedType) paramType;
paramType = par.getActualTypeArguments()[0];
if (paramType instanceof WildcardType) {
// some registry types are even more nested, but we don't want them
// for example Registry<Codec<ChunkGenerator>>, Registry<Codec<WorldChunkManager>>
WildcardType wildcard = (WildcardType) paramType;
if (wildcard.getUpperBounds().length != 1 || wildcard.getLowerBounds().length > 0) {
continue;
}
if (!iRegistry.isAssignableFrom(field.getType())) {
continue;
}
// we only want types with an undefined upper bound (aka. ?)
if (wildcard.getUpperBounds()[0] != Object.class) {
continue;
}
}
Type genType = field.getGenericType();
if (!(genType instanceof ParameterizedType par)) {
continue;
}
paramType = par.getRawType();
if (paramType instanceof Class<?>) {
// there might be duplicate registries, like the codec registries
// we don't want them to be registered here
regMap.put((Class<?>) paramType, new WrappedRegistry(field.get(null)));
}
Class<?> registryClass = null;
Type paramType = par.getActualTypeArguments()[0];
if (paramType instanceof Class<?> paramClass) {
// for example Registry<Item>
registryClass = paramClass;
} else if (paramType instanceof ParameterizedType) {
// for example Registry<EntityType<?>>
par = (ParameterizedType) paramType;
paramType = par.getActualTypeArguments()[0];
if (paramType instanceof WildcardType wildcard) {
// some registry types are even more nested, but we don't want them
// for example Registry<Codec<ChunkGenerator>>, Registry<Codec<WorldChunkManager>>
if (wildcard.getUpperBounds().length != 1 || wildcard.getLowerBounds().length > 0) {
continue;
}
// we only want types with an undefined upper bound (aka. ?)
if (wildcard.getUpperBounds()[0] != Object.class) {
continue;
}
}
paramType = par.getRawType();
if (paramType instanceof Class<?> paramClass) {
// there might be duplicate registries, like the codec registries
// we don't want them to be registered here
registryClass = paramClass;
}
}
Object handle = field.get(null);
ResourceKey key = ResourceKey.fromGeneric(GET_REGISTRY_KEY.invoke(handle));
WrappedRegistry wrapped = new WrappedRegistry(handle, key);
if (registryClass != null) {
byClass.put(registryClass, wrapped);
}
String registryName = key.getLocation().getFullKey();
byKey.put(registryName, wrapped);
} catch (ReflectiveOperationException ignored) {
}
}
}
REGISTRY = ImmutableMap.copyOf(regMap);
FuzzyReflection fuzzy = FuzzyReflection.fromClass(iRegistry, false);
GET = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(1)
.returnTypeMatches(FuzzyMatchers.and(FuzzyMatchers.assignable(Object.class), FuzzyMatchers.except(Optional.class)))
.requireModifier(Modifier.ABSTRACT)
.parameterExactType(MinecraftReflection.getMinecraftKeyClass())
.build()));
GET_ID = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(1)
.returnTypeExact(int.class)
.requireModifier(Modifier.ABSTRACT)
.parameterDerivedOf(Object.class)
.build()));
GET_KEY = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(1)
.returnTypeExact(MinecraftReflection.getMinecraftKeyClass())
.build()));
MethodAccessor getHolder = null;
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
try {
getHolder = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(1)
.banModifier(Modifier.STATIC)
.returnTypeExact(MinecraftReflection.getHolderClass())
.requireModifier(Modifier.PUBLIC)
.build()));
} catch (IllegalArgumentException ignored) {
}
}
GET_HOLDER = getHolder;
BY_CLASS = ImmutableMap.copyOf(byClass);
BY_KEY = ImmutableMap.copyOf(byKey);
}
private final Object handle;
private final ResourceKey key;
private WrappedRegistry(Object handle) {
private WrappedRegistry(Object handle, ResourceKey key) {
this.handle = handle;
this.key = key;
}
public Object get(MinecraftKey key) {
return GET.invoke(handle, MinecraftKey.getConverter().getGeneric(key));
return GET_BY_KEY.invoke(handle, MinecraftKey.getConverter().getGeneric(key));
}
public Object get(String key) {
@@ -139,7 +168,11 @@ public class WrappedRegistry {
}
public MinecraftKey getKey(Object generic) {
return MinecraftKey.getConverter().getSpecific(GET_KEY.invoke(handle, generic));
return MinecraftKey.getConverter().getSpecific(GET_ITEM_KEY.invoke(handle, generic));
}
public ResourceKey getKey() {
return key;
}
public int getId(MinecraftKey key) {
@@ -151,7 +184,7 @@ public class WrappedRegistry {
}
public int getId(Object entry) {
return (int) GET_ID.invoke(this.handle, entry);
return (int) GET_ITEM_ID.invoke(this.handle, entry);
}
public Object getHolder(Object generic) {
@@ -170,7 +203,15 @@ public class WrappedRegistry {
return getRegistry(MinecraftReflection.getSoundEffectClass());
}
public static WrappedRegistry getDataComponentTypeRegistry() {
return getByKey("minecraft:data_component_type");
}
public static WrappedRegistry getRegistry(Class<?> type) {
return REGISTRY.get(type);
return BY_CLASS.get(type);
}
public static WrappedRegistry getByKey(String key) {
return BY_KEY.get(key);
}
}
@@ -1,11 +1,15 @@
package com.comphenix.protocol.wrappers;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.comphenix.protocol.injector.StructureCache;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.EnumWrappers.TeamCollisionRule;
import com.comphenix.protocol.wrappers.EnumWrappers.TeamVisibility;
import com.google.common.base.Preconditions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A wrapper around the team parameters NMS class.
@@ -35,11 +39,16 @@ public class WrappedTeamParameters extends AbstractWrapper {
}
private final StructureModifier<Object> modifier;
private final StructureModifier<TeamVisibility> visiblityModifier;
private final StructureModifier<TeamCollisionRule> collisionRuleModifier;
public WrappedTeamParameters(Object handle) {
super(getNmsClassOrThrow());
setHandle(handle);
this.modifier = new StructureModifier<>(getNmsClassOrThrow()).withTarget(handle);
this.visiblityModifier = this.modifier.withType(EnumWrappers.getTeamVisibilityClass(), EnumWrappers.getTeamVisibilityConverter());
this.collisionRuleModifier = this.modifier.withType(EnumWrappers.getTeamCollisionRuleClass(), EnumWrappers.getTeamCollisionRuleConverter());
}
@NotNull
@@ -58,19 +67,32 @@ public class WrappedTeamParameters extends AbstractWrapper {
}
@NotNull
public String getNametagVisibility() {
return modifier.<String>withType(String.class).read(0);
public TeamVisibility getNametagVisibility() {
TeamVisibility visibility = this.visiblityModifier.readSafely(0);
if (visibility == null) {
return TeamVisibility.fromName(modifier.<String>withType(String.class).read(0));
} else {
return visibility;
}
}
@NotNull
public String getCollisionRule() {
return modifier.<String>withType(String.class).read(1);
public TeamCollisionRule getCollisionRule() {
TeamCollisionRule collisionRule = this.collisionRuleModifier.readSafely(0);
if (collisionRule == null) {
return TeamCollisionRule.fromName(modifier.<String>withType(String.class).read(1));
} else {
return collisionRule;
}
}
@NotNull
public EnumWrappers.ChatFormatting getColor() {
Object handle = modifier.withType(EnumWrappers.getChatFormattingClass()).read(0);
return EnumWrappers.getChatFormattingConverter().getSpecific(handle);
return modifier
.withType(EnumWrappers.getChatFormattingClass(), EnumWrappers.getChatFormattingConverter())
.read(0);
}
public int getOptions() {
@@ -88,7 +110,8 @@ public class WrappedTeamParameters extends AbstractWrapper {
public static class Builder {
private WrappedChatComponent displayName, prefix, suffix;
private String nametagVisibility, collisionRule;
private TeamVisibility nametagVisibility;
private TeamCollisionRule collisionRule;
private EnumWrappers.ChatFormatting color;
private int options;
@@ -122,18 +145,36 @@ public class WrappedTeamParameters extends AbstractWrapper {
return this;
}
public Builder nametagVisibility(@NotNull String nametagVisibility) {
public Builder nametagVisibility(@NotNull TeamVisibility nametagVisibility) {
Preconditions.checkNotNull(nametagVisibility);
this.nametagVisibility = nametagVisibility;
return this;
}
public Builder collisionRule(@NotNull String collisionRule) {
/**
* Compatibility stub for FeatherBoard – accepts raw String visibility.
*/
public Builder nametagVisibility(@NotNull String visibility) {
Preconditions.checkNotNull(visibility, "visibility string cannot be null");
this.nametagVisibility = TeamVisibility.fromName(visibility);
return this;
}
public Builder collisionRule(@NotNull TeamCollisionRule collisionRule) {
Preconditions.checkNotNull(collisionRule);
this.collisionRule = collisionRule;
return this;
}
/**
* Compatibility stub for FeatherBoard – accepts raw String collision rules.
*/
public Builder collisionRule(@NotNull String rule) {
Preconditions.checkNotNull(rule, "collision rule cannot be null");
this.collisionRule = TeamCollisionRule.fromName(rule);
return this;
}
public Builder color(@NotNull EnumWrappers.ChatFormatting color) {
Preconditions.checkNotNull(color);
this.color = color;
@@ -160,8 +201,15 @@ public class WrappedTeamParameters extends AbstractWrapper {
wrapped.writeComponent(0, displayName);
wrapped.writeComponent(1, prefix);
wrapped.writeComponent(2, suffix);
wrapped.modifier.withType(String.class).write(0, nametagVisibility);
wrapped.modifier.withType(String.class).write(1, collisionRule);
if (MinecraftVersion.v1_21_5.atOrAbove()) {
wrapped.visiblityModifier.writeSafely(0, nametagVisibility);
wrapped.collisionRuleModifier.writeSafely(0, collisionRule);
} else {
wrapped.modifier.withType(String.class).writeSafely(0, nametagVisibility.toString());
wrapped.modifier.withType(String.class).writeSafely(1, collisionRule.toString());
}
wrapped.modifier.withType(EnumWrappers.getChatFormattingClass()).write(0, EnumWrappers.getChatFormattingConverter().getGeneric(color));
wrapped.modifier.withType(int.class).write(0, options);
return wrapped;
@@ -23,7 +23,7 @@ public interface NbtList<TType> extends NbtBase<List<NbtBase<TType>>>, Iterable<
String EMPTY_NAME = "";
/**
* Get the type of each element.
* Get the type of each element. For heterogeneous lists this will return {@link NbtType#TAG_COMPOUND TAG_COMPOUND}.
* <p>
* This will be {@link NbtType#TAG_END TAG_END} if the NBT list has just been created.
* @return Element type.
@@ -32,8 +32,12 @@ public interface NbtList<TType> extends NbtBase<List<NbtBase<TType>>>, Iterable<
/**
* Set the type of each element.
* <p>
* In 1.21.5 and up lists can be heterogeneous, here the type is only used to convert elements added with {@link NbtList#addClosest(Object)}
* @param type - type of each element.
* @deprecated since 1.21.5 lists can be heterogeneous
*/
@Deprecated
void setElementType(NbtType type);
/**
@@ -41,9 +45,19 @@ public interface NbtList<TType> extends NbtBase<List<NbtBase<TType>>>, Iterable<
* <p>
* Note that the list must be typed by setting {@link #setElementType(NbtType)} before calling this function.
* @param value - the value to add.
* @deprecated since 1.21.5 lists can be heterogeneous. Use {@link NbtList#addClosest(Object, NbtType)}
*/
@Deprecated
void addClosest(Object value);
/**
* Add a value to a typed list by attempting to convert it to the nearest value.
* <p>
* @param value - the value to add.
* @param type - the resulting type of the entry
*/
void addClosest(Object value, NbtType type);
/**
* Add a NBT list or NBT compound to the list.
* @param element Element to add
@@ -1,29 +1,27 @@
package com.comphenix.protocol.wrappers.nbt;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.block.BlockState;
import com.comphenix.protocol.injector.BukkitUnwrapper;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.ByteBuddyFactory;
import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftRegistryAccess;
import com.comphenix.protocol.utility.MinecraftVersion;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.InvocationHandlerAdapter;
import net.bytebuddy.jar.asm.ClassReader;
import net.bytebuddy.jar.asm.ClassVisitor;
import net.bytebuddy.jar.asm.MethodVisitor;
import net.bytebuddy.jar.asm.Opcodes;
import net.bytebuddy.jar.asm.Type;
import net.bytebuddy.matcher.ElementMatchers;
import org.bukkit.block.BlockState;
/**
* Manipulate tile entities.
@@ -34,6 +32,10 @@ class TileEntityAccessor<T extends BlockState> {
private static final boolean BLOCK_DATA_INCL = MinecraftVersion.NETHER_UPDATE.atOrAbove()
&& !MinecraftVersion.CAVES_CLIFFS_1.atOrAbove();
private static final boolean USE_HOLDER_LOOKUP = MinecraftVersion.v1_21_5.atOrAbove();
private static final boolean USE_TAG_VALUE_INPUT = MinecraftVersion.v1_21_6.atOrAbove();
private static final Class<?> TAG_VALUE_INPUT = MinecraftReflection.getNullableNMS("world.level.storage.TagValueInput");
/**
* Token indicating that the given block state doesn't contain any tile entities.
@@ -45,7 +47,7 @@ class TileEntityAccessor<T extends BlockState> {
*/
private static final Map<Class<?>, TileEntityAccessor<?>> cachedAccessors = new HashMap<>();
private static Constructor<?> nbtCompoundParserConstructor;
private MethodAccessor createInputWrapper;
private FieldAccessor tileEntityField;
private MethodAccessor readCompound;
@@ -104,11 +106,50 @@ class TileEntityAccessor<T extends BlockState> {
}
void findMethods(Class<?> type, T state) {
if (BLOCK_DATA_INCL) {
Class<?> tileEntityClass = MinecraftReflection.getTileEntityClass();
Class<?> iBlockData = MinecraftReflection.getIBlockDataClass();
Class<?> nbtCompound = MinecraftReflection.getNBTCompoundClass();
Class<?> tileEntityClass = MinecraftReflection.getTileEntityClass();
Class<?> nbtCompound = MinecraftReflection.getNBTCompoundClass();
if (USE_HOLDER_LOOKUP) {
Class<?> holderLookup = MinecraftReflection.getHolderLookupProviderClass();
FuzzyReflection fuzzy = FuzzyReflection.fromClass(tileEntityClass, false);
// should be equiv. to `CompoundTag saveWithFullMetadata(HolderLookup.Provider)`
writeCompound = Accessors.getMethodAccessor(fuzzy.getMethod(
FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.requireModifier(Modifier.FINAL)
.returnTypeExact(nbtCompound)
.parameterExactArray(holderLookup)
.build()));
if (USE_TAG_VALUE_INPUT) {
createInputWrapper = Accessors.getMethodAccessor(FuzzyReflection.fromClass(TAG_VALUE_INPUT).getMethod(
FuzzyMethodContract.newBuilder()
.requireModifier(Modifier.STATIC)
.parameterExactArray(MinecraftReflection.getMinecraftClass("util.ProblemReporter"), holderLookup, nbtCompound)
.build()));
// should be equiv. to `void loadWithComponents(ValueInput)`
readCompound = Accessors.getMethodAccessor(fuzzy.getMethod(
FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.requireModifier(Modifier.FINAL)
.returnTypeVoid()
.parameterSuperOf(TAG_VALUE_INPUT)
.build()));
} else {
// should be equiv. to `void loadWithComponents(CompoundTag, HolderLookup.Provider)`
readCompound = Accessors.getMethodAccessor(fuzzy.getMethod(
FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.requireModifier(Modifier.FINAL)
.returnTypeVoid()
.parameterExactArray(nbtCompound, holderLookup)
.build()));
}
} else if (BLOCK_DATA_INCL) {
Class<?> iBlockData = MinecraftReflection.getIBlockDataClass();
FuzzyReflection fuzzy = FuzzyReflection.fromClass(tileEntityClass, false);
writeCompound = Accessors.getMethodAccessor(fuzzy.getMethod(
FuzzyMethodContract.newBuilder()
@@ -214,12 +255,17 @@ class TileEntityAccessor<T extends BlockState> {
* @return The compound.
*/
public NbtCompound readBlockState(T state) {
NbtCompound output = NbtFactory.ofCompound("");
Object tileEntity = tileEntityField.get(state);
// Write the block state to the output compound
writeCompound.invoke(tileEntity, NbtFactory.fromBase(output).getHandle());
return output;
if (USE_HOLDER_LOOKUP) {
Object tag = writeCompound.invoke(tileEntity, MinecraftRegistryAccess.get());
return new WrappedCompound(tag);
} else {
NbtCompound output = NbtFactory.ofCompound("");
writeCompound.invoke(tileEntity, NbtFactory.fromBase(output).getHandle());
return output;
}
}
/**
@@ -232,7 +278,14 @@ class TileEntityAccessor<T extends BlockState> {
Object tileEntity = tileEntityField.get(state);
// Ensure the block state is set to the compound
if (BLOCK_DATA_INCL) {
if (USE_HOLDER_LOOKUP) {
if (USE_TAG_VALUE_INPUT) {
Object tagValueInput = createInputWrapper.invoke(null, null, MinecraftRegistryAccess.get(), NbtFactory.fromBase(compound).getHandle());
readCompound.invoke(tileEntity, tagValueInput);
} else {
readCompound.invoke(tileEntity, NbtFactory.fromBase(compound).getHandle(), MinecraftRegistryAccess.get());
}
} else if (BLOCK_DATA_INCL) {
Object blockData = BukkitUnwrapper.getInstance().unwrapItem(state);
readCompound.invoke(tileEntity, blockData, NbtFactory.fromBase(compound).getHandle());
} else {
@@ -17,6 +17,7 @@
package com.comphenix.protocol.wrappers.nbt;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.collection.ConvertedList;
import com.comphenix.protocol.wrappers.nbt.io.NbtBinarySerializer;
import com.google.common.base.Joiner;
@@ -103,7 +104,9 @@ class WrappedList<TType> implements NbtWrapper<List<NbtBase<TType>>>, NbtList<TT
*/
public WrappedList(Object handle) {
this.container = new WrappedElement<>(handle);
this.elementType = container.getSubType();
if (!MinecraftVersion.v1_21_5.atOrAbove()) {
this.elementType = container.getSubType();
}
}
/**
@@ -113,7 +116,9 @@ class WrappedList<TType> implements NbtWrapper<List<NbtBase<TType>>>, NbtList<TT
*/
public WrappedList(Object handle, String name) {
this.container = new WrappedElement<>(handle, name);
this.elementType = container.getSubType();
if (!MinecraftVersion.v1_21_5.atOrAbove()) {
this.elementType = container.getSubType();
}
}
@Override
@@ -141,13 +146,28 @@ class WrappedList<TType> implements NbtWrapper<List<NbtBase<TType>>>, NbtList<TT
@Override
public NbtType getElementType() {
return elementType;
if (!MinecraftVersion.v1_21_5.atOrAbove()) {
return elementType;
} else {
List<NbtBase<TType>> value = getValue();
NbtType type = NbtType.TAG_END;
for (NbtBase<TType> entry : value) {
if (type == NbtType.TAG_END) {
type = entry.getType();
} else if (type != entry.getType()) {
return NbtType.TAG_COMPOUND;
}
}
return type;
}
}
@Override
public void setElementType(NbtType type) {
this.elementType = type;
container.setSubType(type);
if (!MinecraftVersion.v1_21_5.atOrAbove()) {
container.setSubType(type);
}
}
@Override
@@ -171,14 +191,16 @@ class WrappedList<TType> implements NbtWrapper<List<NbtBase<TType>>>, NbtList<TT
if (!element.getName().equals(EMPTY_NAME))
throw new IllegalArgumentException("Cannot add a the named NBT tag " + element + " to a list.");
// Check element type
if (getElementType() != NbtType.TAG_END) {
if (!element.getType().equals(getElementType())) {
throw new IllegalArgumentException(
"Cannot add " + element + " of " + element.getType() + " to a list of type " + getElementType());
if (!MinecraftVersion.v1_21_5.atOrAbove()) {
// Check element type
if (getElementType() != NbtType.TAG_END) {
if (!element.getType().equals(getElementType())) {
throw new IllegalArgumentException(
"Cannot add " + element + " of " + element.getType() + " to a list of type " + getElementType());
}
} else {
container.setSubType(element.getType());
}
} else {
container.setSubType(element.getType());
}
}
@@ -235,16 +257,21 @@ class WrappedList<TType> implements NbtWrapper<List<NbtBase<TType>>>, NbtList<TT
}
@Override
@SuppressWarnings("unchecked")
public void addClosest(Object value) {
if (getElementType() == NbtType.TAG_END)
if (elementType == NbtType.TAG_END)
throw new IllegalStateException("This list has not been typed yet.");
addClosest(value, elementType);
}
@Override
@SuppressWarnings("unchecked")
public void addClosest(Object value, NbtType type) {
if (value instanceof Number) {
Number number = (Number) value;
// Convert the number
switch (getElementType()) {
switch (type) {
case TAG_BYTE: add(number.byteValue()); break;
case TAG_SHORT: add(number.shortValue()); break;
case TAG_INT: add(number.intValue()); break;
@@ -262,7 +289,7 @@ class WrappedList<TType> implements NbtWrapper<List<NbtBase<TType>>>, NbtList<TT
} else {
// Just add it
add((NbtBase<TType>) NbtFactory.ofWrapper(getElementType(), EMPTY_NAME, value));
add((NbtBase<TType>) NbtFactory.ofWrapper(type, EMPTY_NAME, value));
}
}
@@ -355,7 +382,7 @@ class WrappedList<TType> implements NbtWrapper<List<NbtBase<TType>>>, NbtList<TT
}
// Update the sub type as well
if (lastElement != null) {
if (lastElement != null && !MinecraftVersion.v1_21_5.atOrAbove()) {
container.setSubType(lastElement.getType());
}
}
@@ -261,10 +261,9 @@ public class NbtConfigurationSerializer {
if (node instanceof List) {
NbtList<Object> list = NbtFactory.ofList(decoded[0]);
list.setElementType(type);
for (Object value : (List<Object>) node) {
list.addClosest(toNodeValue(value, type));
list.addClosest(toNodeValue(value, type), type);
}
// Add the list
+6
View File
@@ -0,0 +1,6 @@
name: ProtocolLib
version: ${version}
main: com.comphenix.protocol.ProtocolLibPaper
api-version: '1.21.8'
website: https://github.com/dmulloy2/ProtocolLib
load: STARTUP
+1 -1
View File
@@ -3,7 +3,7 @@ version: ${version}
description: Provides read/write access to the Minecraft protocol.
authors: [dmulloy2, comphenix]
main: com.comphenix.protocol.ProtocolLib
main: com.comphenix.protocol.ProtocolLibSpigot
load: STARTUP
database: false
api-version: "1.13"
@@ -1,8 +1,16 @@
package com.comphenix.integration.protocol;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.reflect.ExactReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import org.apache.logging.log4j.Logger;
@@ -13,13 +21,6 @@ import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginLoadOrder;
import org.bukkit.plugin.PluginManager;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -1,10 +1,5 @@
package com.comphenix.integration.protocol;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.utility.StreamSerializer;
import com.google.common.io.ByteStreams;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
@@ -15,6 +10,13 @@ import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.utility.StreamSerializer;
import com.google.common.io.ByteStreams;
public class SimpleMinecraftClient {
private static final int CONNECT_TIMEOUT = 2500;
@@ -1,17 +1,18 @@
package com.comphenix.integration.protocol;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketEvent;
import org.bukkit.plugin.Plugin;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketEvent;
import org.bukkit.plugin.Plugin;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestPingPacket {
@@ -13,50 +13,51 @@ import com.comphenix.protocol.utility.MinecraftReflectionTestUtil;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.MoreExecutors;
import net.minecraft.SharedConstants;
import net.minecraft.commands.CommandDispatcher;
import net.minecraft.core.IRegistry;
import net.minecraft.core.IRegistryCustom;
import net.minecraft.commands.Commands;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.LayeredRegistryAccess;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.MinecraftKey;
import net.minecraft.resources.RegistryDataLoader;
import net.minecraft.server.DataPackResources;
import net.minecraft.server.DispenserRegistry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.Bootstrap;
import net.minecraft.server.RegistryLayer;
import net.minecraft.server.WorldLoader;
import net.minecraft.server.ReloadableServerResources;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.WorldServer;
import net.minecraft.server.packs.EnumResourcePackType;
import net.minecraft.server.packs.repository.ResourcePackLoader;
import net.minecraft.server.packs.repository.ResourcePackRepository;
import net.minecraft.server.packs.repository.ResourcePackSourceVanilla;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.packs.PackType;
import net.minecraft.server.packs.repository.Pack;
import net.minecraft.server.packs.repository.PackRepository;
import net.minecraft.server.packs.repository.ServerPacksSource;
import net.minecraft.server.packs.resources.MultiPackResourceManager;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.tags.TagKey;
import net.minecraft.world.entity.EntityTypes;
import net.minecraft.tags.TagLoader;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.flag.FeatureFlags;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.enchantment.Enchantments;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.material.FluidType;
import net.minecraft.world.level.material.Fluid;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_21_R1.CraftLootTable;
import org.bukkit.craftbukkit.v1_21_R1.CraftRegistry;
import org.bukkit.craftbukkit.v1_21_R1.CraftServer;
import org.bukkit.craftbukkit.v1_21_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_21_R1.tag.CraftBlockTag;
import org.bukkit.craftbukkit.v1_21_R1.tag.CraftEntityTag;
import org.bukkit.craftbukkit.v1_21_R1.tag.CraftFluidTag;
import org.bukkit.craftbukkit.v1_21_R1.tag.CraftItemTag;
import org.bukkit.craftbukkit.v1_21_R1.util.CraftMagicNumbers;
import org.bukkit.craftbukkit.v1_21_R1.util.CraftNamespacedKey;
import org.bukkit.craftbukkit.v1_21_R1.util.Versioning;
import org.bukkit.craftbukkit.v1_21_R5.CraftLootTable;
import org.bukkit.craftbukkit.v1_21_R5.CraftRegistry;
import org.bukkit.craftbukkit.v1_21_R5.CraftServer;
import org.bukkit.craftbukkit.v1_21_R5.CraftWorld;
import org.bukkit.craftbukkit.v1_21_R5.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_21_R5.tag.CraftBlockTag;
import org.bukkit.craftbukkit.v1_21_R5.tag.CraftEntityTag;
import org.bukkit.craftbukkit.v1_21_R5.tag.CraftFluidTag;
import org.bukkit.craftbukkit.v1_21_R5.tag.CraftItemTag;
import org.bukkit.craftbukkit.v1_21_R5.util.CraftMagicNumbers;
import org.bukkit.craftbukkit.v1_21_R5.util.CraftNamespacedKey;
import org.bukkit.craftbukkit.v1_21_R5.util.Versioning;
import org.jetbrains.annotations.NotNull;
import org.spigotmc.SpigotWorldConfig;
@@ -111,36 +112,42 @@ public class BukkitInitialization {
instance.setPackage();
// Minecraft Data Init
SharedConstants.a(); // .tryDetectVersion()
DispenserRegistry.a(); // .bootStrap()
SharedConstants.tryDetectVersion(); // .tryDetectVersion()
Bootstrap.bootStrap(); // .bootStrap()
ResourcePackRepository resourcePackRepository = ResourcePackSourceVanilla.c(); // .createVanillaTrustedRepository()
resourcePackRepository.a(); // .reload()
PackRepository repo = ServerPacksSource.createVanillaTrustedRepository();
repo.reload();
ResourceManager resourceManager = new ResourceManager(
EnumResourcePackType.b /* SERVER_DATA */,
resourcePackRepository.c() /* getAvailablePacks() */ .stream().map(ResourcePackLoader::f /* openFull() */).collect(Collectors.toList()));
LayeredRegistryAccess<RegistryLayer> layeredRegistryAccess = RegistryLayer.a(); // .createRegistryAccess()
layeredRegistryAccess = WorldLoader.b(resourceManager, layeredRegistryAccess, RegistryLayer.b /* WORLDGEN */, RegistryDataLoader.a /* WORLDGEN_REGISTRIES */); // .loadAndReplaceLayer()
IRegistryCustom.Dimension registryCustom = layeredRegistryAccess.a().d(); // .compositeAccess().freeze()
// IRegistryCustom.Dimension registryCustom = layeredRegistryAccess.a().c(); // .compositeAccess().freeze()
ResourceManager resourceManager = new MultiPackResourceManager(
PackType.SERVER_DATA,
repo.getAvailablePacks().stream().map(Pack::open).collect(Collectors.toList())
);
DataPackResources dataPackResources = DataPackResources.a(
resourceManager,
layeredRegistryAccess,
FeatureFlags.d.a() /* REGISTRY.allFlags() */,
CommandDispatcher.ServerType.b /* DEDICATED */,
0,
MoreExecutors.directExecutor(),
MoreExecutors.directExecutor()
LayeredRegistryAccess<RegistryLayer> layeredAccess1 = RegistryLayer.createRegistryAccess();
List<net.minecraft.core.Registry.PendingTags<?>> tags = TagLoader.loadTagsForExistingRegistries(resourceManager, layeredAccess1.getLayer(RegistryLayer.STATIC));
RegistryAccess.Frozen access1 = layeredAccess1.getAccessForLoading(RegistryLayer.WORLDGEN);
List<HolderLookup.RegistryLookup<?>> list1 = TagLoader.buildUpdatedLookups(access1, tags);
RegistryAccess.Frozen access2 = RegistryDataLoader.load(resourceManager, list1, RegistryDataLoader.WORLDGEN_REGISTRIES);
LayeredRegistryAccess<RegistryLayer> layeredAccess2 = layeredAccess1.replaceFrom(RegistryLayer.WORLDGEN, access2);
RegistryAccess.Frozen registryCustom = layeredAccess2.compositeAccess().freeze();
ReloadableServerResources dataPackResources = ReloadableServerResources.loadResources(
resourceManager,
layeredAccess2,
tags,
FeatureFlags.REGISTRY.allFlags() /* REGISTRY.allFlags() */,
Commands.CommandSelection.DEDICATED /* DEDICATED */,
0,
MoreExecutors.directExecutor(),
MoreExecutors.directExecutor()
).join();
dataPackResources.g();
dataPackResources.updateStaticRegistryTags();
try {
/* try {
IRegistry.class.getName();
} catch (Throwable ex) {
ex.printStackTrace();
}
} */
String releaseTarget = MinecraftReflectionTestUtil.RELEASE_TARGET;
String serverVersion = CraftServer.class.getPackage().getImplementationVersion();
@@ -149,7 +156,7 @@ public class BukkitInitialization {
CraftServer mockedServer = mock(CraftServer.class);
DedicatedServer mockedGameServer = mock(DedicatedServer.class);
when(mockedGameServer.bc()/*registryAccess*/).thenReturn(registryCustom);
when(mockedGameServer.registryAccess()).thenReturn(registryCustom);
when(mockedServer.getLogger()).thenReturn(java.util.logging.Logger.getLogger("Minecraft"));
when(mockedServer.getName()).thenReturn("Mock Server");
@@ -162,7 +169,7 @@ public class BukkitInitialization {
when(mockedServer.getUnsafe()).thenReturn(CraftMagicNumbers.INSTANCE);
when(mockedServer.getLootTable(any())).thenAnswer(invocation -> {
NamespacedKey key = invocation.getArgument(0);
return new CraftLootTable(key, dataPackResources.b().b(CraftLootTable.bukkitKeyToMinecraft(key)));
return new CraftLootTable(key, dataPackResources.fullRegistries().getLootTable(CraftLootTable.bukkitKeyToMinecraft(key)));
});
when(mockedServer.getRegistry(any())).thenAnswer(invocation -> {
Class<Keyed> registryType = invocation.getArgument(0);
@@ -179,35 +186,35 @@ public class BukkitInitialization {
when(mockedServer.getTag(any(), any(), any())).then(mock -> {
String registry = mock.getArgument(0);
Class<?> clazz = mock.getArgument(2);
MinecraftKey key = CraftNamespacedKey.toMinecraft(mock.getArgument(1));
ResourceLocation key = CraftNamespacedKey.toMinecraft(mock.getArgument(1));
switch (registry) {
case org.bukkit.Tag.REGISTRY_BLOCKS -> {
Preconditions.checkArgument(clazz == org.bukkit.Material.class, "Block namespace must have block type");
TagKey<Block> blockTagKey = TagKey.a(Registries.f, key);
if (BuiltInRegistries.e.b(blockTagKey).isPresent()) {
return new CraftBlockTag(BuiltInRegistries.e, blockTagKey);
TagKey<Block> blockTagKey = TagKey.create(Registries.BLOCK, key);
if (BuiltInRegistries.BLOCK.get(blockTagKey).isPresent()) {
return new CraftBlockTag(BuiltInRegistries.BLOCK, blockTagKey);
}
}
case org.bukkit.Tag.REGISTRY_ITEMS -> {
Preconditions.checkArgument(clazz == org.bukkit.Material.class, "Item namespace must have item type");
TagKey<Item> itemTagKey = TagKey.a(Registries.K, key);
if (BuiltInRegistries.g.b(itemTagKey).isPresent()) {
return new CraftItemTag(BuiltInRegistries.g, itemTagKey);
TagKey<Item> itemTagKey = TagKey.create(Registries.ITEM, key);
if (BuiltInRegistries.ITEM.get(itemTagKey).isPresent()) {
return new CraftItemTag(BuiltInRegistries.ITEM, itemTagKey);
}
}
case org.bukkit.Tag.REGISTRY_FLUIDS -> {
Preconditions.checkArgument(clazz == org.bukkit.Fluid.class, "Fluid namespace must have fluid type");
TagKey<FluidType> fluidTagKey = TagKey.a(Registries.D, key);
if (BuiltInRegistries.c.b(fluidTagKey).isPresent()) {
return new CraftFluidTag(BuiltInRegistries.c, fluidTagKey);
TagKey<Fluid> fluidTagKey = TagKey.create(Registries.FLUID, key);
if (BuiltInRegistries.FLUID.get(fluidTagKey).isPresent()) {
return new CraftFluidTag(BuiltInRegistries.FLUID, fluidTagKey);
}
}
case org.bukkit.Tag.REGISTRY_ENTITY_TYPES -> {
Preconditions.checkArgument(clazz == org.bukkit.entity.EntityType.class, "Entity type namespace must have entity type");
TagKey<EntityTypes<?>> entityTagKey = TagKey.a(Registries.z, key);
if (BuiltInRegistries.f.b(entityTagKey).isPresent()) {
return new CraftEntityTag(BuiltInRegistries.f, entityTagKey);
TagKey<EntityType<?>> entityTagKey = TagKey.create(Registries.ENTITY_TYPE, key);
if (BuiltInRegistries.ENTITY_TYPE.get(entityTagKey).isPresent()) {
return new CraftEntityTag(BuiltInRegistries.ENTITY_TYPE, entityTagKey);
}
}
default -> throw new IllegalArgumentException();
@@ -216,7 +223,7 @@ public class BukkitInitialization {
return null;
});
WorldServer nmsWorld = mock(WorldServer.class);
ServerLevel nmsWorld = mock(ServerLevel.class);
SpigotWorldConfig mockWorldConfig = mock(SpigotWorldConfig.class);
try {
@@ -237,8 +244,7 @@ public class BukkitInitialization {
CraftRegistry.setMinecraftRegistry(registryCustom);
// Init Enchantments
Enchantments.A.getClass();
// Enchantment.stopAcceptingRegistrations();
Enchantments.AQUA_AFFINITY.getClass();
initialized = true;
}
@@ -16,10 +16,12 @@ package com.comphenix.protocol;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -28,15 +30,12 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import com.comphenix.protocol.PacketType.Play.Server;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import net.minecraft.network.protocol.login.PacketLoginInStart;
import net.minecraft.network.protocol.login.ServerboundHelloPacket;
/**
* @author dmulloy2
@@ -62,7 +61,6 @@ public class PacketTypeTest {
}
public static void main(String[] args) throws Exception {
// public void generateNewPackets() throws Exception {
BukkitInitialization.initializeAll();
PacketType.onDynamicCreate = (type, className) -> {
@@ -256,11 +254,21 @@ public class PacketTypeTest {
assertEquals(type, roundTrip);
}
}
@Test
public void testPacketCodec() {
Set<PacketType> notSerializable = Set.of(PacketType.Play.Server.BUNDLE);
for (PacketType type : PacketType.values()) {
if (!notSerializable.contains(type) && type.isSupported()) {
assertNotNull(PacketRegistry.getStreamCodec(type.getPacketClass()), "missing serializer for " + type);
}
}
}
@Test
public void testLoginStart() {
// This packet is critical for handleLoin
assertEquals(PacketLoginInStart.class, PacketType.Login.Client.START.getPacketClass());
assertEquals(ServerboundHelloPacket.class, PacketType.Login.Client.START.getPacketClass());
}
@Test
@@ -1,6 +1,7 @@
package com.comphenix.protocol.collections;
import com.comphenix.protocol.utility.IntegerMath;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -1,18 +1,16 @@
package com.comphenix.protocol.concurrent;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.bukkit.plugin.Plugin;
import org.junit.jupiter.api.Test;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.google.common.collect.ImmutableSet;
import org.bukkit.plugin.Plugin;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class PacketTypeListenerSetTest {
@@ -1,20 +1,17 @@
package com.comphenix.protocol.concurrent;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.junit.jupiter.api.Test;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.google.common.collect.ImmutableSet;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class PacketTypeMultiMapTest {
@@ -1,15 +1,12 @@
package com.comphenix.protocol.concurrent;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class SortedCopyOnWriteSetTest {
@Test
@@ -15,6 +15,18 @@
*/
package com.comphenix.protocol.events;
import static com.comphenix.protocol.utility.TestUtils.assertItemCollectionsEqual;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static com.comphenix.protocol.utility.TestUtils.equivalentItem;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
@@ -29,6 +41,23 @@ import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
import net.minecraft.network.protocol.game.ServerboundPlayerCommandPacket;
import org.apache.commons.lang.SerializationUtils;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.injector.PacketConstructor;
@@ -65,45 +94,28 @@ import com.comphenix.protocol.wrappers.WrappedRegistry;
import com.comphenix.protocol.wrappers.WrappedRemoteChatSessionData;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.google.common.collect.Lists;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.hover.content.Text;
import net.minecraft.core.Holder;
import net.minecraft.core.IRegistry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.protocol.game.PacketPlayOutGameStateChange;
import net.minecraft.network.protocol.game.PacketPlayOutUpdateAttributes;
import net.minecraft.network.protocol.game.PacketPlayOutUpdateAttributes.AttributeSnapshot;
import net.minecraft.resources.MinecraftKey;
import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket;
import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket.AttributeSnapshot;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectList;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.ai.attributes.AttributeBase;
import net.minecraft.world.entity.ai.attributes.Attribute;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.AttributeModifier.Operation;
import net.minecraft.world.entity.animal.CatVariant;
import net.minecraft.world.entity.animal.FrogVariant;
import org.apache.commons.lang.SerializationUtils;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static com.comphenix.protocol.utility.TestUtils.*;
import static org.junit.jupiter.api.Assertions.*;
import net.minecraft.world.entity.animal.CatVariants;
import net.minecraft.world.entity.animal.frog.FrogVariant;
import net.minecraft.world.entity.animal.frog.FrogVariants;
public class PacketContainerTest {
@@ -182,13 +194,13 @@ public class PacketContainerTest {
this.testPrimitive(updateTime.getLongs(), 0, (long) 0, (long) 1);
}
@Test
// @Test // TODO: Explosion no longer contains floats
public void testGetFloat() {
PacketContainer explosion = new PacketContainer(PacketType.Play.Server.EXPLOSION);
this.testPrimitive(explosion.getFloat(), 0, (float) 0, (float) 0.8);
}
@Test
// @Test // TODO: Explosion no longer contains doubles
public void testGetDoubles() {
PacketContainer explosion = new PacketContainer(PacketType.Play.Server.EXPLOSION);
this.testPrimitive(explosion.getDoubles(), 0, (double) 0, 0.8);
@@ -225,7 +237,7 @@ public class PacketContainerTest {
@Test
public void testGetItemModifier() {
PacketContainer windowClick = new PacketContainer(PacketType.Play.Client.WINDOW_CLICK);
PacketContainer windowClick = new PacketContainer(PacketType.Play.Server.SET_CURSOR_ITEM);
ItemStack item = this.itemWithData();
@@ -318,7 +330,7 @@ public class PacketContainerTest {
assertEquals(packet.getEntityTypeModifier().read(0), EntityType.ARROW);
}
@Test
// @Test // TODO: explosion no longer contains block position list
public void testGetPositionCollectionModifier() {
PacketContainer explosionPacket = new PacketContainer(PacketType.Play.Server.EXPLOSION);
StructureModifier<List<BlockPosition>> positionAccessor = explosionPacket.getBlockPositionCollectionModifier();
@@ -511,16 +523,16 @@ public class PacketContainerTest {
// Initialize some test data
List<AttributeModifier> modifiers = Lists.newArrayList(
new AttributeModifier(MinecraftKey.a("protocollib:test"),10,
AttributeModifier.Operation.a));
new AttributeModifier(ResourceLocation.parse("protocollib:test"),10,
Operation.ADD_VALUE));
// Obtain an AttributeSnapshot instance. This is complicated by the fact that AttributeSnapshots
// are inner classes (which is ultimately pointless because AttributeSnapshots don't access any
// members of the packet itself)
PacketPlayOutUpdateAttributes packet = (PacketPlayOutUpdateAttributes) attribute.getHandle();
IRegistry<AttributeBase> registry = BuiltInRegistries.s;
AttributeBase base = registry.a(MinecraftKey.a("generic.max_health"));
AttributeSnapshot snapshot = new AttributeSnapshot(Holder.a(base), 20.0D, modifiers);
ClientboundUpdateAttributesPacket packet = (ClientboundUpdateAttributesPacket) attribute.getHandle();
net.minecraft.core.Registry<Attribute> registry = BuiltInRegistries.ATTRIBUTE;
Attribute base = registry.getValue(ResourceLocation.parse("max_health"));
AttributeSnapshot snapshot = new AttributeSnapshot(registry.wrapAsHolder(base), 20.0D, modifiers);
attribute.getSpecificModifier(List.class).write(0, Lists.newArrayList(snapshot));
PacketContainer cloned = attribute.deepClone();
@@ -569,14 +581,14 @@ public class PacketContainerTest {
@SuppressWarnings("deprecation")
public void testPotionEffect() {
PotionEffect effect = new PotionEffect(PotionEffectType.FIRE_RESISTANCE, 20 * 60, 1);
MobEffect mobEffect = new MobEffect(MobEffects.l, effect.getDuration(),
MobEffectInstance mobEffect = new MobEffectInstance(MobEffects.FIRE_RESISTANCE, effect.getDuration(),
effect.getAmplifier(), effect.isAmbient(),
effect.hasParticles());
int entityId = 42;
// The constructor we want to call
PacketConstructor creator = PacketConstructor.DEFAULT.withPacket(
PacketType.Play.Server.ENTITY_EFFECT, new Class<?>[] { int.class, MobEffect.class, boolean.class });
PacketType.Play.Server.ENTITY_EFFECT, new Class<?>[] { int.class, MobEffectInstance.class, boolean.class });
PacketContainer packet = creator.createPacket(entityId, mobEffect, true);
assertEquals(entityId, packet.getIntegers().read(0));
@@ -586,8 +598,8 @@ public class PacketContainerTest {
WrappedRegistry registry = WrappedRegistry.getRegistry(MinecraftReflection.getMobEffectListClass());
Object effectList = assertInstanceOf(
MobEffectList.class,
packet.getHolders(MobEffectList.class, InternalStructure.CONVERTER).read(0).getHandle()
MobEffect.class,
packet.getHolders(MobEffect.class, InternalStructure.CONVERTER).read(0).getHandle()
);
assertEquals(effect.getType().getId(), registry.getId(effectList) + 1); // +1 is correct, see CraftPotionEffectType
@@ -615,10 +627,6 @@ public class PacketContainerTest {
// no change across nms versions
container.getPlayerActions().write(0, EnumWrappers.PlayerAction.OPEN_INVENTORY);
assertEquals(container.getPlayerActions().read(0), EnumWrappers.PlayerAction.OPEN_INVENTORY);
// changed in 1.15
container.getPlayerActions().write(0, EnumWrappers.PlayerAction.START_SNEAKING);
assertEquals(container.getPlayerActions().read(0), EnumWrappers.PlayerAction.START_SNEAKING);
}
@Test
@@ -698,7 +706,7 @@ public class PacketContainerTest {
@Test
public void testMovingBlockPos() {
PacketContainer container = new PacketContainer(PacketType.Play.Client.USE_ITEM);
PacketContainer container = new PacketContainer(PacketType.Play.Client.USE_ITEM_ON);
Vector vector = new Vector(0, 1, 2);
BlockPosition position = new BlockPosition(3, 4, 5);
@@ -911,7 +919,6 @@ public class PacketContainerTest {
// Make sure watchable collections can be cloned
if (type == PacketType.Play.Server.ENTITY_METADATA) {
IRegistry<CatVariant> catVariantRegistry = BuiltInRegistries.ai;
constructed.getDataValueCollectionModifier().write(0, Lists.newArrayList(
new WrappedDataValue(0, Registry.get(Byte.class), (byte) 1),
new WrappedDataValue(0, Registry.get(Float.class), 5F),
@@ -925,8 +932,8 @@ public class PacketContainerTest {
0,
Registry.getItemStackSerializer(false),
BukkitConverters.getItemStackConverter().getGeneric(new ItemStack(Material.WOODEN_AXE))),
new WrappedDataValue(0, Registry.get(CatVariant.class), catVariantRegistry.e(CatVariant.e)),
new WrappedDataValue(0, Registry.get(FrogVariant.class), FrogVariant.a)
new WrappedDataValue(0, Registry.get(CatVariant.class), CatVariants.RED),
new WrappedDataValue(0, Registry.get(FrogVariant.class), FrogVariants.COLD)
));
} else if (type == PacketType.Play.Server.CHAT || type == PacketType.Login.Server.DISCONNECT) {
constructed.getChatComponents().write(0, ComponentConverter.fromBaseComponent(TEST_COMPONENT));
@@ -935,7 +942,7 @@ public class PacketContainerTest {
} else if (type == PacketType.Play.Server.GAME_STATE_CHANGE) {
constructed.getStructures().write(
0,
InternalStructure.getConverter().getSpecific(PacketPlayOutGameStateChange.a));
InternalStructure.getConverter().getSpecific(ClientboundGameEventPacket.WIN_GAME));
} else if (type == PacketType.Play.Client.USE_ITEM || type == PacketType.Play.Client.BLOCK_PLACE) {
constructed.getLongs().write(0, 0L); // timestamp of the packet, not sent over the network
}
@@ -1,11 +1,7 @@
package com.comphenix.protocol.events;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.when;
import java.util.UUID;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.junit.jupiter.api.BeforeEach;
@@ -13,6 +9,9 @@ import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
public class SerializedOfflinePlayerTest {
private static final String name = "playerName";
@@ -1,26 +1,28 @@
package com.comphenix.protocol.injector;
import java.lang.reflect.Field;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minecraft.server.level.ChunkProviderServer;
import net.minecraft.server.level.PlayerChunkMap;
import net.minecraft.server.level.PlayerChunkMap.EntityTracker;
import net.minecraft.server.level.WorldServer;
import net.minecraft.world.entity.Entity;
import org.bukkit.craftbukkit.v1_21_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_21_R1.entity.CraftEntity;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static com.comphenix.protocol.utility.TestUtils.setFinalField;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.lang.reflect.Field;
import org.bukkit.craftbukkit.v1_21_R5.CraftWorld;
import org.bukkit.craftbukkit.v1_21_R5.entity.CraftEntity;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.ai.behavior.EntityTracker;
public class EntityUtilitiesTest {
@BeforeAll
@@ -31,15 +33,15 @@ public class EntityUtilitiesTest {
@Test
public void testReflection() {
CraftWorld bukkit = mock(CraftWorld.class);
WorldServer world = mock(WorldServer.class);
ServerLevel world = mock(ServerLevel.class);
when(bukkit.getHandle()).thenReturn(world);
ChunkProviderServer provider = mock(ChunkProviderServer.class);
when(world.l()).thenReturn(provider);
ServerChunkCache provider = mock(ServerChunkCache.class);
when(world.getChunkSource()).thenReturn(provider);
PlayerChunkMap chunkMap = mock(PlayerChunkMap.class);
Field chunkMapField = FuzzyReflection.fromClass(ChunkProviderServer.class, true)
.getField(FuzzyFieldContract.newBuilder().typeExact(PlayerChunkMap.class).build());
ChunkMap chunkMap = mock(ChunkMap.class);
Field chunkMapField = FuzzyReflection.fromClass(ServerChunkCache.class, true)
.getField(FuzzyFieldContract.newBuilder().typeExact(ChunkMap.class).build());
setFinalField(provider, chunkMapField, chunkMap);
CraftEntity bukkitEntity = mock(CraftEntity.class);
@@ -53,7 +55,7 @@ public class EntityUtilitiesTest {
Int2ObjectMap<EntityTracker> trackerMap = new Int2ObjectOpenHashMap<>();
trackerMap.put(1, tracker);
Field trackedEntitiesField = FuzzyReflection.fromClass(PlayerChunkMap.class, true)
Field trackedEntitiesField = FuzzyReflection.fromClass(ChunkMap.class, true)
.getField(FuzzyFieldContract.newBuilder().typeExact(Int2ObjectMap.class).build());
setFinalField(chunkMap, trackedEntitiesField, trackerMap);
}
@@ -1,17 +1,12 @@
package com.comphenix.protocol.injector;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.comphenix.protocol.injector.PluginVerifier.VerificationResult;
import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.comphenix.protocol.injector.PluginVerifier.VerificationResult;
import com.google.common.base.Objects;
import org.bukkit.Server;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
@@ -21,6 +16,11 @@ import org.junit.jupiter.api.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
// Damn final classes
class PluginVerifierTest {
@@ -3,19 +3,21 @@
*/
package com.comphenix.protocol.injector;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.ArrayList;
import java.util.List;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.netty.WirePacket;
import io.netty.buffer.ByteBuf;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @author dmulloy2
*/
@@ -1,15 +1,8 @@
package com.comphenix.protocol.injector.netty.channel;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import io.netty.channel.Channel;
import io.netty.channel.local.LocalServerChannel;
import net.minecraft.network.EnumProtocol;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.protocol.EnumProtocolDirection;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class ChannelProtocolUtilTest {
@@ -1,11 +1,11 @@
package com.comphenix.protocol.injector.netty.channel;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import com.comphenix.protocol.BukkitInitialization;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import com.comphenix.protocol.BukkitInitialization;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
public class PacketListenerInvokerTest {
@@ -1,7 +1,5 @@
package com.comphenix.protocol.injector.temporary;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.junit.jupiter.api.BeforeEach;
@@ -9,6 +7,8 @@ import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class TemporaryPlayerFactoryTest {
@Mock
@@ -1,10 +1,11 @@
package com.comphenix.protocol.reflect.accessors;
import com.comphenix.protocol.reflect.ExactReflection;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Field;
import com.comphenix.protocol.reflect.ExactReflection;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -1,18 +1,15 @@
package com.comphenix.protocol.reflect.cloning;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import java.util.Arrays;
import java.util.List;
import net.minecraft.core.NonNullList;
import net.minecraft.world.item.ItemStack;
import org.junit.jupiter.api.Assertions;
import com.comphenix.protocol.BukkitInitialization;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class AggregateClonerTest {
@BeforeAll
@@ -1,20 +1,20 @@
package com.comphenix.protocol.reflect.instances;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.util.UUID;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import net.minecraft.core.NonNullList;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.ItemStack;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minecraft.core.NonNullList;
import net.minecraft.world.entity.EntityTypes;
import net.minecraft.world.item.ItemStack;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class MinecraftGeneratorTest {
@@ -28,7 +28,7 @@ public class MinecraftGeneratorTest {
assertNotNull(MinecraftGenerator.INSTANCE.create(UUID.class));
assertNotNull(MinecraftGenerator.INSTANCE.create(PacketType.Protocol.class));
assertNotNull(MinecraftGenerator.INSTANCE.create(ItemStack.class));
assertNotNull(MinecraftGenerator.INSTANCE.create(EntityTypes.class));
assertNotNull(MinecraftGenerator.INSTANCE.create(EntityType.class));
assertNotNull(MinecraftGenerator.INSTANCE.create(Int2ObjectMap.class));
assertNotNull(MinecraftGenerator.INSTANCE.create(Int2ObjectOpenHashMap.class));
assertNotNull(MinecraftGenerator.INSTANCE.create(NonNullList.class));
@@ -3,19 +3,21 @@
*/
package com.comphenix.protocol.updater;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.logging.Logger;
import com.comphenix.protocol.updater.Updater.UpdateType;
import java.util.logging.Logger;
import org.bukkit.Server;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @author dmulloy2
*/
@@ -1,13 +1,13 @@
package com.comphenix.protocol.utility;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.lang.reflect.Field;
import com.comphenix.protocol.BukkitInitialization;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import com.comphenix.protocol.BukkitInitialization;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class MinecraftMethodsTest {
@@ -1,19 +1,21 @@
package com.comphenix.protocol.utility;
import com.comphenix.protocol.BukkitInitialization;
import com.mojang.authlib.GameProfile;
import net.minecraft.nbt.NBTCompressedStreamTools;
import net.minecraft.network.chat.IChatBaseComponent;
import net.minecraft.network.protocol.game.PacketPlayOutUpdateAttributes;
import net.minecraft.network.protocol.status.ServerPing;
import net.minecraft.network.syncher.DataWatcher;
import net.minecraft.server.network.PlayerConnection;
import net.minecraft.util.MinecraftEncryption;
import net.minecraft.world.level.ChunkCoordIntPair;
import net.minecraft.world.level.block.state.IBlockData;
import net.minecraft.nbt.NbtIo;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.ComponentSerialization;
import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket;
import net.minecraft.network.protocol.status.ServerStatus;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.util.Crypt;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_21_R5.inventory.CraftItemStack;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.junit.jupiter.api.AfterAll;
@@ -21,13 +23,9 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
public class MinecraftReflectionTest {
@@ -66,63 +64,63 @@ public class MinecraftReflectionTest {
@Test
public void testAttributeSnapshot() {
assertEquals(PacketPlayOutUpdateAttributes.AttributeSnapshot.class,
assertEquals(ClientboundUpdateAttributesPacket.AttributeSnapshot.class,
MinecraftReflection.getAttributeSnapshotClass());
}
@Test
public void testChatComponent() {
assertEquals(IChatBaseComponent.class, MinecraftReflection.getIChatBaseComponentClass());
assertEquals(Component.class, MinecraftReflection.getIChatBaseComponentClass());
}
@Test
public void testChatSerializer() {
assertEquals(IChatBaseComponent.ChatSerializer.class, MinecraftReflection.getChatSerializerClass());
assertEquals(ComponentSerialization.class, MinecraftReflection.getChatSerializerClass());
}
@Test
public void testChunkCoordIntPair() {
assertEquals(ChunkCoordIntPair.class, MinecraftReflection.getChunkCoordIntPair());
assertEquals(ChunkPos.class, MinecraftReflection.getChunkCoordIntPair());
}
@Test
public void testIBlockData() {
assertEquals(IBlockData.class, MinecraftReflection.getIBlockDataClass());
assertEquals(BlockState.class, MinecraftReflection.getIBlockDataClass());
}
@Test
public void testPlayerConnection() {
assertEquals(PlayerConnection.class, MinecraftReflection.getPlayerConnectionClass());
assertEquals(ServerGamePacketListenerImpl.class, MinecraftReflection.getPlayerConnectionClass());
}
@Test
public void testServerPing() {
assertEquals(ServerPing.class, MinecraftReflection.getServerPingClass());
assertEquals(ServerStatus.class, MinecraftReflection.getServerPingClass());
}
@Test
public void testServerPingPlayerSample() {
assertEquals(ServerPing.ServerPingPlayerSample.class, MinecraftReflection.getServerPingPlayerSampleClass());
assertEquals(ServerStatus.Players.class, MinecraftReflection.getServerPingPlayerSampleClass());
}
@Test
public void testServerPingServerData() {
assertEquals(ServerPing.ServerData.class, MinecraftReflection.getServerPingServerDataClass());
assertEquals(ServerStatus.Version.class, MinecraftReflection.getServerPingServerDataClass());
}
@Test
public void testNbtStreamTools() {
assertEquals(NBTCompressedStreamTools.class, MinecraftReflection.getNbtCompressedStreamToolsClass());
assertEquals(NbtIo.class, MinecraftReflection.getNbtCompressedStreamToolsClass());
}
@Test
public void testDataWatcherItem() {
assertEquals(DataWatcher.Item.class, MinecraftReflection.getDataWatcherItemClass());
assertEquals(SynchedEntityData.DataItem.class, MinecraftReflection.getDataWatcherItemClass());
}
@Test
public void testLoginSignature() {
assertEquals(MinecraftEncryption.b.class, MinecraftReflection.getSaltedSignatureClass());
assertEquals(Crypt.SaltSignaturePair.class, MinecraftReflection.getSaltedSignatureClass());
}
@Test
@@ -2,8 +2,8 @@ package com.comphenix.protocol.utility;
public class MinecraftReflectionTestUtil {
public static final String RELEASE_TARGET = "1.20.6";
public static final String PACKAGE_VERSION = "v1_21_R1";
public static final String RELEASE_TARGET = "1.21.6";
public static final String PACKAGE_VERSION = "v1_21_R5";
public static final String NMS = "net.minecraft";
public static final String OBC = "org.bukkit.craftbukkit." + PACKAGE_VERSION;
@@ -0,0 +1,12 @@
package com.comphenix.protocol.utility;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class MinecraftRegistryAccessTest {
@Test
public void testGet() {
assertNotNull(MinecraftRegistryAccess.get());
}
}
@@ -48,7 +48,7 @@ class MinecraftVersionTest {
@Test
void testCurrent() {
assertEquals(MinecraftVersion.v1_21_0, MinecraftVersion.getCurrentVersion());
assertEquals(MinecraftVersion.v1_21_6, MinecraftVersion.getCurrentVersion());
}
@Test
@@ -1,13 +1,14 @@
package com.comphenix.protocol.utility;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class SnapshotVersionTest {
@Test
@@ -1,24 +1,26 @@
package com.comphenix.protocol.utility;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class StreamSerializerTest {
@BeforeAll
@@ -1,11 +1,5 @@
package com.comphenix.protocol.utility;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.comphenix.protocol.reflect.accessors.Accessors;
import java.lang.reflect.Field;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
@@ -14,11 +8,15 @@ import java.util.List;
import java.util.Random;
import java.util.UUID;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey;
import com.comphenix.protocol.wrappers.WrappedRemoteChatSessionData;
import org.bukkit.Bukkit;
import org.bukkit.inventory.ItemStack;
import static org.junit.jupiter.api.Assertions.*;
public class TestUtils {
public static void assertItemCollectionsEqual(List<ItemStack> first, List<ItemStack> second) {
@@ -1,22 +1,28 @@
package com.comphenix.protocol.wrappers;
import static com.comphenix.protocol.utility.MinecraftReflection.getMinecraftClass;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Optional;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.utility.MinecraftReflection;
import net.minecraft.advancements.AdvancementDisplay;
import net.minecraft.advancements.AdvancementFrameType;
import net.minecraft.network.chat.IChatBaseComponent;
import net.minecraft.network.chat.contents.LiteralContents;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static com.comphenix.protocol.utility.MinecraftReflection.getMinecraftClass;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.utility.MinecraftReflection;
import static org.junit.jupiter.api.Assertions.*;
import net.minecraft.advancements.AdvancementType;
import net.minecraft.advancements.DisplayInfo;
import net.minecraft.core.ClientAsset;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.contents.PlainTextContents;
import net.minecraft.resources.ResourceLocation;
public class AutoWrapperTest {
@@ -31,7 +37,7 @@ public class AutoWrapperTest {
display.title = WrappedChatComponent.fromText("Test123");
display.description = WrappedChatComponent.fromText("Test567");
display.item = new ItemStack(Material.GOLD_INGOT);
display.background = Optional.of(new MinecraftKey("test"));
display.background = Optional.of(new ClientAsset(ResourceLocation.fromNamespaceAndPath("minecraft", "test")));
display.frameType = WrappedFrameType.CHALLENGE;
display.announceChat = false;
display.showToast = true;
@@ -39,34 +45,34 @@ public class AutoWrapperTest {
display.x = 5f;
display.y = 67f;
AdvancementDisplay nms = (AdvancementDisplay) displayWrapper().unwrap(display);
DisplayInfo nms = (DisplayInfo) displayWrapper().unwrap(display);
assertTrue(nms.h());
assertTrue(nms.j());
assertFalse(nms.i());
assertTrue(nms.d().isPresent());
assertEquals("test", nms.d().get().a());
validateRawText(nms.a(), "Test123");
validateRawText(nms.b(), "Test567");
assertSame(AdvancementFrameType.b, nms.e());
assertSame(MinecraftReflection.getBukkitItemStack(nms.c()).getType(), Material.GOLD_INGOT);
assertEquals(5f, nms.f(), 0f);
assertEquals(67f, nms.g(), 0f);
assertTrue(nms.shouldShowToast());
assertTrue(nms.isHidden());
assertFalse(nms.shouldAnnounceChat());
assertTrue(nms.getBackground().isPresent());
assertEquals("test", nms.getBackground().get().id().getPath());
validateRawText(nms.getTitle(), "Test123");
validateRawText(nms.getDescription(), "Test567");
assertSame(AdvancementType.CHALLENGE, nms.getType());
assertSame(MinecraftReflection.getBukkitItemStack(nms.getIcon()).getType(), Material.GOLD_INGOT);
assertEquals(5f, nms.getX(), 0f);
assertEquals(67f, nms.getY(), 0f);
}
@Test
public void testFromNms() {
AdvancementDisplay display = new AdvancementDisplay(
DisplayInfo display = new DisplayInfo(
(net.minecraft.world.item.ItemStack)MinecraftReflection.getMinecraftItemStack(new ItemStack(Material.ENDER_EYE)),
IChatBaseComponent.b("Test123"),
IChatBaseComponent.b("Test567"),
Optional.of(net.minecraft.resources.MinecraftKey.a("minecraft", "test")),
AdvancementFrameType.b,
Component.literal("Test123"),
Component.literal("Test567"),
Optional.of(new ClientAsset(ResourceLocation.fromNamespaceAndPath("minecraft", "test"))),
AdvancementType.CHALLENGE,
true,
false,
true
);
display.a(5f, 67f);
display.setLocation(5f, 67f);
WrappedAdvancementDisplay wrapped = displayWrapper().wrap(display);
@@ -74,7 +80,7 @@ public class AutoWrapperTest {
assertTrue(wrapped.hidden);
assertFalse(wrapped.announceChat);
assertTrue(wrapped.background.isPresent());
assertEquals("test", wrapped.background.get().getKey());
assertEquals("test", wrapped.background.get().id().getPath());
assertEquals("\"Test123\"", wrapped.title.getJson());
assertEquals("\"Test567\"", wrapped.description.getJson());
assertSame(WrappedFrameType.CHALLENGE, wrapped.frameType);
@@ -89,14 +95,16 @@ public class AutoWrapperTest {
.field(0, BukkitConverters.getWrappedChatComponentConverter())
.field(1, BukkitConverters.getWrappedChatComponentConverter())
.field(2, BukkitConverters.getItemStackConverter())
.field(3, Converters.optional(MinecraftKey.getConverter()))
.field(4, EnumWrappers.getGenericConverter(getMinecraftClass("advancements.AdvancementFrameType", "advancements.FrameType"),
WrappedFrameType.class));
.field(3, Converters.optional(Converters.passthrough(ClientAsset.class)))
.field(4, EnumWrappers.getGenericConverter(
getMinecraftClass("advancements.AdvancementType", "advancements.AdvancementFrameType", "advancements.FrameType"),
WrappedFrameType.class)
);
}
private void validateRawText(IChatBaseComponent component, String expected) {
LiteralContents content = assertInstanceOf(LiteralContents.class, component.b());
assertEquals(expected, content.b());
private void validateRawText(Component component, String expected) {
PlainTextContents.LiteralContents content = assertInstanceOf(PlainTextContents.LiteralContents.class, component.getContents());
assertEquals(expected, content.text());
}
public enum WrappedFrameType {
@@ -110,7 +118,7 @@ public class AutoWrapperTest {
public WrappedChatComponent title;
public WrappedChatComponent description;
public ItemStack item;
public Optional<MinecraftKey> background;
public Optional<ClientAsset> background;
public WrappedFrameType frameType;
public boolean showToast;
public boolean announceChat;
@@ -1,11 +1,14 @@
package com.comphenix.protocol.wrappers;
import java.util.Random;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.utility.TestUtils;
import com.comphenix.protocol.wrappers.Either.Left;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
@@ -17,8 +20,6 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.util.Random;
import static org.junit.jupiter.api.Assertions.*;
public class BukkitConvertersTest {
@@ -1,11 +1,12 @@
package com.comphenix.protocol.wrappers;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ChunkCoordIntPairTest {
@BeforeAll
@@ -15,17 +16,17 @@ public class ChunkCoordIntPairTest {
@Test
public void test() {
net.minecraft.world.level.ChunkCoordIntPair pair = new net.minecraft.world.level.ChunkCoordIntPair(1, 2);
net.minecraft.world.level.ChunkPos pair = new net.minecraft.world.level.ChunkPos(1, 2);
ChunkCoordIntPair specific = ChunkCoordIntPair.getConverter().getSpecific(pair);
assertEquals(1, specific.getChunkX());
assertEquals(2, specific.getChunkZ());
net.minecraft.world.level.ChunkCoordIntPair roundtrip =
(net.minecraft.world.level.ChunkCoordIntPair) ChunkCoordIntPair.getConverter().
net.minecraft.world.level.ChunkPos roundtrip =
(net.minecraft.world.level.ChunkPos) ChunkCoordIntPair.getConverter().
getGeneric(specific);
assertEquals(1, roundtrip.e);
assertEquals(2, roundtrip.f);
assertEquals(1, roundtrip.x);
assertEquals(2, roundtrip.z);
}
}
@@ -1,12 +1,13 @@
package com.comphenix.protocol.wrappers;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.reflect.cloning.AggregateCloner;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class CloningTest {
@BeforeAll
@@ -1,15 +1,14 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.EquivalentConverter;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.comphenix.protocol.reflect.EquivalentConverter;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* @author Lukas Alt
@@ -1,10 +1,10 @@
package com.comphenix.protocol.wrappers;
import java.util.Optional;
import com.comphenix.protocol.wrappers.Either.Left;
import com.comphenix.protocol.wrappers.Either.Right;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
Loaded 100 of 125 files, more files were not shown because too many files have changed in this diff. Show more