forked from upstream/ProtocolLib
Compare commits
45
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39808bc64f | ||
|
|
771a530fe2 | ||
|
|
ca9b5bbb80 | ||
|
|
d790ded81f | ||
|
|
f357ac85d0 | ||
|
|
66cdb5b69d | ||
|
|
1aeb002afe | ||
|
|
79d44f0d6e | ||
|
|
9bde9b4231 | ||
|
|
714252df6f | ||
|
|
e56bcf624d | ||
|
|
596e5e4246 | ||
|
|
f3f895995a | ||
|
|
67e7375b2f | ||
|
|
9417eee444 | ||
|
|
a432fae4dd | ||
|
|
8a5e59d4f2 | ||
|
|
90a386b743 | ||
|
|
a80762feed | ||
|
|
a936183b76 | ||
|
|
804aa5df11 | ||
|
|
1e73df8600 | ||
|
|
4314a55866 | ||
|
|
2c0d632dc2 | ||
|
|
fd5d8c576e | ||
|
|
0256953056 | ||
|
|
24262d3f7d | ||
|
|
7a85663ded | ||
|
|
bcff6c6719 | ||
|
|
3155e76d06 | ||
|
|
6845acd89d | ||
|
|
3442d3fb8f | ||
|
|
1e3fa2d36e | ||
|
|
c9ee2f16e2 | ||
|
|
7f615378e4 | ||
|
|
d636be81e7 | ||
|
|
3cb261f466 | ||
|
|
7338183ed6 | ||
|
|
fb085676a9 | ||
|
|
9fc68d4b6c | ||
|
|
f227b17f5f | ||
|
|
4aa344bab2 | ||
|
|
da65c9d3c1 | ||
|
|
47bcd53fd0 | ||
|
|
2cad996252 |
No files matched your search
@@ -1,18 +1,20 @@
|
||||
name: ProtocolLib full build lifecycle
|
||||
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'gh-pages'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '21'
|
||||
@@ -22,7 +24,7 @@ jobs:
|
||||
run: ./gradlew build shadowJar --no-daemon
|
||||
|
||||
- name: Upload plugin file
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ProtocolLib
|
||||
path: build/libs/ProtocolLib.jar
|
||||
@@ -20,17 +20,17 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '21'
|
||||
cache: 'gradle'
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: 'java'
|
||||
|
||||
@@ -38,4 +38,4 @@ jobs:
|
||||
run: ./gradlew build -x test --no-daemon
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
@@ -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 }}
|
||||
@@ -181,4 +181,3 @@ logs/
|
||||
build/
|
||||
logs/
|
||||
.java-version
|
||||
gradle.properties
|
||||
@@ -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
@@ -1,130 +0,0 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'maven-publish'
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1'
|
||||
}
|
||||
|
||||
group = 'com.comphenix.protocol'
|
||||
version = '5.3.0-SNAPSHOT'
|
||||
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.14.14'
|
||||
compileOnly 'org.spigotmc:spigot-api:1.20.6-R0.1-SNAPSHOT'
|
||||
compileOnly 'org.spigotmc:spigot:1.21-R0.1-SNAPSHOT:remapped-mojang'
|
||||
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-R0.1-SNAPSHOT:remapped-mojang'
|
||||
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'
|
||||
}
|
||||
@@ -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
@@ -1,2 +1,6 @@
|
||||
nexusUsername=""
|
||||
nexusPassword=""
|
||||
version=5.4.1-SNAPSHOT
|
||||
mavenCentralUsername=
|
||||
mavenCentralPassword=
|
||||
signing.keyId=
|
||||
signing.password=
|
||||
signing.secretKeyRingFile=
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -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,66 +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");
|
||||
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x39, "UseItem");
|
||||
|
||||
// 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
|
||||
@@ -613,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");
|
||||
@@ -692,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
|
||||
@@ -723,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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,8 @@ package com.comphenix.protocol;
|
||||
import com.comphenix.protocol.error.BasicErrorReporter;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.scheduler.ProtocolScheduler;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import java.util.List;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.apache.commons.lang.Validate;
|
||||
|
||||
import com.comphenix.protocol.utility.Validate;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
@@ -37,17 +35,12 @@ public class ProtocolLibrary {
|
||||
/**
|
||||
* The maximum version ProtocolLib has been tested with.
|
||||
*/
|
||||
public static final String MAXIMUM_MINECRAFT_VERSION = "1.20.4";
|
||||
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 = "2023-12-07";
|
||||
|
||||
/**
|
||||
* Plugins that are currently incompatible with ProtocolLib.
|
||||
*/
|
||||
public static final List<String> INCOMPATIBLE = ImmutableList.of("TagAPI");
|
||||
public static final String MINECRAFT_LAST_RELEASE_DATE = "2025-07-17";
|
||||
|
||||
private static Plugin plugin;
|
||||
private static ProtocolConfig config;
|
||||
@@ -55,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) {
|
||||
|
||||
@@ -17,14 +17,6 @@
|
||||
|
||||
package com.comphenix.protocol.async;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.AsynchronousManager;
|
||||
import com.comphenix.protocol.PacketStream;
|
||||
import com.comphenix.protocol.PacketType;
|
||||
@@ -37,8 +29,16 @@ import com.comphenix.protocol.injector.collection.InboundPacketListenerSet;
|
||||
import com.comphenix.protocol.injector.collection.OutboundPacketListenerSet;
|
||||
import com.comphenix.protocol.scheduler.ProtocolScheduler;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Represents a filter manager for asynchronous packets.
|
||||
@@ -284,9 +284,9 @@ public class AsyncFilterManager implements AsynchronousManager {
|
||||
}
|
||||
|
||||
private void unregisterAsyncHandlers(PacketProcessingQueue processingQueue, Plugin plugin) {
|
||||
|
||||
|
||||
// Iterate through every packet listener
|
||||
for (AsyncListenerHandler listener : processingQueue.values()) {
|
||||
for (AsyncListenerHandler listener : ImmutableList.copyOf(processingQueue.values())) {
|
||||
// Remove the listener
|
||||
if (Objects.equal(listener.getPlugin(), plugin)) {
|
||||
unregisterAsyncHandler(listener);
|
||||
|
||||
@@ -607,8 +607,6 @@ public class AsyncListenerHandler {
|
||||
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
// Minecraft doesn't want your Exception.
|
||||
filterManager.getErrorReporter().reportMinimal(listener.getPlugin(), methodName, e);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,494 +0,0 @@
|
||||
/*
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol.concurrency;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.Range;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.NavigableMap;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Represents a generic store of intervals and associated values. No two intervals
|
||||
* can overlap in this representation.
|
||||
* <p>
|
||||
* Note that this implementation is not thread safe.
|
||||
*
|
||||
* @author Kristian
|
||||
*
|
||||
* @param <TKey> - type of the key. Must implement Comparable.
|
||||
* @param <TValue> - type of the value to associate.
|
||||
*/
|
||||
public abstract class AbstractIntervalTree<TKey extends Comparable<TKey>, TValue> {
|
||||
|
||||
protected enum State {
|
||||
OPEN,
|
||||
CLOSE,
|
||||
BOTH
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a range and a value in this interval tree.
|
||||
*/
|
||||
public class Entry implements Map.Entry<Range<TKey>, TValue> {
|
||||
private final EndPoint left;
|
||||
private final EndPoint right;
|
||||
|
||||
Entry(EndPoint left, EndPoint right) {
|
||||
if (left == null)
|
||||
throw new IllegalAccessError("left cannot be NUll");
|
||||
if (right == null)
|
||||
throw new IllegalAccessError("right cannot be NUll");
|
||||
if (left.key.compareTo(right.key) > 0)
|
||||
throw new IllegalArgumentException(
|
||||
"Left key (" + left.key + ") cannot be greater than the right key (" + right.key + ")");
|
||||
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Range<TKey> getKey() {
|
||||
return Range.closed(left.key, right.key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TValue getValue() {
|
||||
return left.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TValue setValue(TValue value) {
|
||||
TValue old = left.value;
|
||||
|
||||
// Set both end points
|
||||
left.value = value;
|
||||
right.value = value;
|
||||
return old;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
// Quick equality check
|
||||
if (obj == this) {
|
||||
return true;
|
||||
} else if (obj instanceof AbstractIntervalTree.Entry) {
|
||||
return Objects.equal(left.key, ((AbstractIntervalTree.Entry) obj).left.key) &&
|
||||
Objects.equal(right.key, ((AbstractIntervalTree.Entry) obj).right.key) &&
|
||||
Objects.equal(left.value, ((AbstractIntervalTree.Entry) obj).left.value);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(left.key, right.key, left.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Value %s at [%s, %s]", left.value, left.key, right.key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a single end point (open, close or both) of a range.
|
||||
*/
|
||||
protected class EndPoint {
|
||||
|
||||
// Whether or not the end-point is opening a range, closing a range or both.
|
||||
public State state;
|
||||
|
||||
// The value this range contains
|
||||
public TValue value;
|
||||
|
||||
// The key of this end point
|
||||
public TKey key;
|
||||
|
||||
public EndPoint(State state, TKey key, TValue value) {
|
||||
this.state = state;
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
// To quickly look up ranges we'll index them by endpoints
|
||||
protected NavigableMap<TKey, EndPoint> bounds = new TreeMap<>();
|
||||
|
||||
/**
|
||||
* Removes every interval that intersects with the given range.
|
||||
* @param lowerBound - lowest value to remove.
|
||||
* @param upperBound - highest value to remove.
|
||||
* @return Intervals that were removed
|
||||
*/
|
||||
public Set<Entry> remove(TKey lowerBound, TKey upperBound) {
|
||||
return remove(lowerBound, upperBound, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes every interval that intersects with the given range.
|
||||
* @param lowerBound - lowest value to remove.
|
||||
* @param upperBound - highest value to remove.
|
||||
* @param preserveDifference - whether or not to preserve the intervals that are partially outside.
|
||||
* @return Intervals that were removed
|
||||
*/
|
||||
public Set<Entry> remove(TKey lowerBound, TKey upperBound, boolean preserveDifference) {
|
||||
checkBounds(lowerBound, upperBound);
|
||||
NavigableMap<TKey, EndPoint> range = bounds.subMap(lowerBound, true, upperBound, true);
|
||||
|
||||
EndPoint first = getNextEndPoint(lowerBound, true);
|
||||
EndPoint last = getPreviousEndPoint(upperBound, true);
|
||||
|
||||
// Used while resizing intervals
|
||||
EndPoint previous = null;
|
||||
EndPoint next = null;
|
||||
|
||||
Set<Entry> resized = new HashSet<>();
|
||||
Set<Entry> removed = new HashSet<>();
|
||||
|
||||
// Remove the previous element too. A close end-point must be preceded by an OPEN end-point.
|
||||
if (first != null && first.state == State.CLOSE) {
|
||||
previous = getPreviousEndPoint(first.key, false);
|
||||
|
||||
// Add the interval back
|
||||
if (previous != null) {
|
||||
removed.add(getEntry(previous, first));
|
||||
}
|
||||
}
|
||||
|
||||
// Get the closing element too.
|
||||
if (last != null && last.state == State.OPEN) {
|
||||
next = getNextEndPoint(last.key, false);
|
||||
|
||||
if (next != null) {
|
||||
removed.add(getEntry(last, next));
|
||||
}
|
||||
}
|
||||
|
||||
// Now remove both ranges
|
||||
removeEntrySafely(previous, first);
|
||||
removeEntrySafely(last, next);
|
||||
|
||||
// Add new resized intervals
|
||||
if (preserveDifference) {
|
||||
if (previous != null) {
|
||||
resized.add(putUnsafe(previous.key, decrementKey(lowerBound), previous.value));
|
||||
}
|
||||
if (next != null) {
|
||||
resized.add(putUnsafe(incrementKey(upperBound), next.key, next.value));
|
||||
}
|
||||
}
|
||||
|
||||
// Get the removed entries too
|
||||
getEntries(removed, range);
|
||||
invokeEntryRemoved(removed);
|
||||
|
||||
if (preserveDifference) {
|
||||
invokeEntryAdded(resized);
|
||||
}
|
||||
|
||||
// Remove the range as well
|
||||
range.clear();
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the entry from a given set of end points.
|
||||
* @param left - leftmost end point.
|
||||
* @param right - rightmost end point.
|
||||
* @return The associated entry.
|
||||
*/
|
||||
protected Entry getEntry(EndPoint left, EndPoint right) {
|
||||
if (left == null)
|
||||
throw new IllegalArgumentException("left endpoint cannot be NULL.");
|
||||
if (right == null)
|
||||
throw new IllegalArgumentException("right endpoint cannot be NULL.");
|
||||
|
||||
// Make sure the order is correct
|
||||
if (right.key.compareTo(left.key) < 0) {
|
||||
return getEntry(right, left);
|
||||
} else {
|
||||
return new Entry(left, right);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeEntrySafely(EndPoint left, EndPoint right) {
|
||||
if (left != null && right != null) {
|
||||
bounds.remove(left.key);
|
||||
bounds.remove(right.key);
|
||||
}
|
||||
}
|
||||
|
||||
// Adds a given end point
|
||||
protected EndPoint addEndPoint(TKey key, TValue value, State state) {
|
||||
EndPoint endPoint = bounds.get(key);
|
||||
|
||||
if (endPoint != null) {
|
||||
endPoint.state = State.BOTH;
|
||||
} else {
|
||||
endPoint = new EndPoint(state, key, value);
|
||||
bounds.put(key, endPoint);
|
||||
}
|
||||
return endPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates a given interval of keys with a certain value. Any previous
|
||||
* association will be overwritten in the given interval.
|
||||
* <p>
|
||||
* Overlapping intervals are not permitted. A key can only be associated with a single value.
|
||||
*
|
||||
* @param lowerBound - the minimum key (inclusive).
|
||||
* @param upperBound - the maximum key (inclusive).
|
||||
* @param value - the value, or NULL to reset this range.
|
||||
*/
|
||||
public void put(TKey lowerBound, TKey upperBound, TValue value) {
|
||||
// While we don't permit overlapping intervals, we'll still allow overwriting existing intervals.
|
||||
remove(lowerBound, upperBound, true);
|
||||
invokeEntryAdded(putUnsafe(lowerBound, upperBound, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates a given interval without performing any interval checks.
|
||||
* @param lowerBound - the minimum key (inclusive).
|
||||
* @param upperBound - the maximum key (inclusive).
|
||||
* @param value - the value, or NULL to reset the range.
|
||||
*/
|
||||
private Entry putUnsafe(TKey lowerBound, TKey upperBound, TValue value) {
|
||||
// OK. Add the end points now
|
||||
if (value != null) {
|
||||
EndPoint left = addEndPoint(lowerBound, value, State.OPEN);
|
||||
EndPoint right = addEndPoint(upperBound, value, State.CLOSE);
|
||||
|
||||
return new Entry(left, right);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to verify the validity of the given interval.
|
||||
* @param lowerBound - lower bound (inclusive).
|
||||
* @param upperBound - upper bound (inclusive).
|
||||
*/
|
||||
private void checkBounds(TKey lowerBound, TKey upperBound) {
|
||||
if (lowerBound == null)
|
||||
throw new IllegalAccessError("lowerbound cannot be NULL.");
|
||||
if (upperBound == null)
|
||||
throw new IllegalAccessError("upperBound cannot be NULL.");
|
||||
if (upperBound.compareTo(lowerBound) < 0)
|
||||
throw new IllegalArgumentException("upperBound cannot be less than lowerBound.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given key is within an interval.
|
||||
* @param key - key to check.
|
||||
* @return TRUE if the given key is within an interval in this tree, FALSE otherwise.
|
||||
*/
|
||||
public boolean containsKey(TKey key) {
|
||||
return getEndPoint(key) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumerates over every range in this interval tree.
|
||||
* @return Number of ranges.
|
||||
*/
|
||||
public Set<Entry> entrySet() {
|
||||
// Don't mind the Java noise
|
||||
Set<Entry> result = new HashSet<>();
|
||||
getEntries(result, bounds);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove every interval.
|
||||
*/
|
||||
public void clear() {
|
||||
if (!bounds.isEmpty()) {
|
||||
remove(bounds.firstKey(), bounds.lastKey());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a map of end points into a set of entries.
|
||||
* @param destination - set of entries.
|
||||
* @param map - a map of end points.
|
||||
*/
|
||||
private void getEntries(Set<Entry> destination, NavigableMap<TKey, EndPoint> map) {
|
||||
Map.Entry<TKey, EndPoint> last = null;
|
||||
|
||||
for (Map.Entry<TKey, EndPoint> entry : map.entrySet()) {
|
||||
switch (entry.getValue().state) {
|
||||
case BOTH:
|
||||
EndPoint point = entry.getValue();
|
||||
destination.add(new Entry(point, point));
|
||||
break;
|
||||
case CLOSE:
|
||||
if (last != null) {
|
||||
destination.add(new Entry(last.getValue(), entry.getValue()));
|
||||
}
|
||||
break;
|
||||
case OPEN:
|
||||
// We don't know the full range yet
|
||||
last = entry;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Illegal open/close state detected.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts every range from the given tree into the current tree.
|
||||
* @param other - the other tree to read from.
|
||||
*/
|
||||
public void putAll(AbstractIntervalTree<TKey, TValue> other) {
|
||||
// Naively copy every range.
|
||||
for (Entry entry : other.entrySet()) {
|
||||
put(entry.left.key, entry.right.key, entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value of the range that matches the given key, or NULL if nothing was found.
|
||||
* @param key - the level to read for.
|
||||
* @return The correct amount of experience, or NULL if nothing was recorded.
|
||||
*/
|
||||
public TValue get(TKey key) {
|
||||
EndPoint point = getEndPoint(key);
|
||||
|
||||
if (point != null)
|
||||
return point.value;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the left-most end-point associated with this key.
|
||||
* @param key - key to search for.
|
||||
* @return The end point found, or NULL.
|
||||
*/
|
||||
protected EndPoint getEndPoint(TKey key) {
|
||||
EndPoint ends = bounds.get(key);
|
||||
|
||||
if (ends != null) {
|
||||
// Always return the end point to the left
|
||||
if (ends.state == State.CLOSE) {
|
||||
Map.Entry<TKey, EndPoint> left = bounds.floorEntry(decrementKey(key));
|
||||
return left != null ? left.getValue() : null;
|
||||
|
||||
} else {
|
||||
return ends;
|
||||
}
|
||||
|
||||
} else {
|
||||
// We need to determine if the point intersects with a range
|
||||
Map.Entry<TKey, EndPoint> left = bounds.floorEntry(key);
|
||||
|
||||
// We only need to check to the left
|
||||
if (left != null && left.getValue().state == State.OPEN) {
|
||||
return left.getValue();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the previous end point of a given key.
|
||||
* @param point - the point to search with.
|
||||
* @param inclusive - whether or not to include the current point in the search.
|
||||
* @return The previous end point of a given given key, or NULL if not found.
|
||||
*/
|
||||
protected EndPoint getPreviousEndPoint(TKey point, boolean inclusive) {
|
||||
if (point != null) {
|
||||
Map.Entry<TKey, EndPoint> previous = bounds.floorEntry(inclusive ? point : decrementKey(point));
|
||||
|
||||
if (previous != null)
|
||||
return previous.getValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next end point of a given key.
|
||||
* @param point - the point to search with.
|
||||
* @param inclusive - whether or not to include the current point in the search.
|
||||
* @return The next end point of a given given key, or NULL if not found.
|
||||
*/
|
||||
protected EndPoint getNextEndPoint(TKey point, boolean inclusive) {
|
||||
if (point != null) {
|
||||
Map.Entry<TKey, EndPoint> next = bounds.ceilingEntry(inclusive ? point : incrementKey(point));
|
||||
|
||||
if (next != null)
|
||||
return next.getValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void invokeEntryAdded(Entry added) {
|
||||
if (added != null) {
|
||||
onEntryAdded(added);
|
||||
}
|
||||
}
|
||||
|
||||
private void invokeEntryAdded(Set<Entry> added) {
|
||||
for (Entry entry : added) {
|
||||
onEntryAdded(entry);
|
||||
}
|
||||
}
|
||||
|
||||
private void invokeEntryRemoved(Set<Entry> removed) {
|
||||
for (Entry entry : removed) {
|
||||
onEntryRemoved(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// Listeners for added or removed entries
|
||||
/**
|
||||
* Invoked when an entry is added.
|
||||
* @param added - the entry that was added.
|
||||
*/
|
||||
protected void onEntryAdded(Entry added) { }
|
||||
|
||||
/**
|
||||
* Invoked when an entry is removed.
|
||||
* @param removed - the removed entry.
|
||||
*/
|
||||
protected void onEntryRemoved(Entry removed) { }
|
||||
|
||||
// Helpers for decrementing or incrementing key values
|
||||
/**
|
||||
* Decrement the given key by one unit.
|
||||
* @param key - the key that should be decremented.
|
||||
* @return The new decremented key.
|
||||
*/
|
||||
protected abstract TKey decrementKey(TKey key);
|
||||
|
||||
/**
|
||||
* Increment the given key by one unit.
|
||||
* @param key - the key that should be incremented.
|
||||
* @return The new incremented key.
|
||||
*/
|
||||
protected abstract TKey incrementKey(TKey key);
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
/*
|
||||
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
|
||||
* Copyright (C) 2012 Kristian S. Stangeland
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program;
|
||||
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
package com.comphenix.protocol.concurrency;
|
||||
|
||||
import com.comphenix.protocol.utility.SafeCacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.RemovalCause;
|
||||
import com.google.common.cache.RemovalListener;
|
||||
import com.google.common.cache.RemovalNotification;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* A map that supports blocking on read operations. Null keys are not supported.
|
||||
* <p>
|
||||
* Values are stored as weak references, and will be automatically removed once they've all been dereferenced.
|
||||
* <p>
|
||||
* @author Kristian
|
||||
*
|
||||
* @param <TKey> - type of the key.
|
||||
* @param <TValue> - type of the value.
|
||||
*/
|
||||
public class BlockingHashMap<TKey, TValue> {
|
||||
// Map of values
|
||||
private final ConcurrentMap<TKey, TValue> backingMap;
|
||||
|
||||
// Map of locked objects
|
||||
private final ConcurrentMap<TKey, Object> locks;
|
||||
|
||||
/**
|
||||
* Retrieve a cache loader that will always throw an exception.
|
||||
* @param <TKey> Type of the key
|
||||
* @param <TValue> Type of the value
|
||||
* @return An invalid cache loader.
|
||||
*/
|
||||
public static <TKey, TValue> CacheLoader<TKey, TValue> newInvalidCacheLoader() {
|
||||
return new CacheLoader<TKey, TValue>() {
|
||||
@Override
|
||||
public TValue load(TKey key) throws Exception {
|
||||
throw new IllegalStateException("Illegal use. Access the map directly instead.");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a new map.
|
||||
*/
|
||||
public BlockingHashMap() {
|
||||
backingMap = SafeCacheBuilder.<TKey, TValue>newBuilder().
|
||||
weakValues().
|
||||
removalListener(
|
||||
new RemovalListener<TKey, TValue>() {
|
||||
@Override
|
||||
public void onRemoval(RemovalNotification<TKey, TValue> entry) {
|
||||
// Clean up locks too
|
||||
if (entry.getCause() != RemovalCause.REPLACED) {
|
||||
locks.remove(entry.getKey());
|
||||
}
|
||||
}
|
||||
}).
|
||||
build(BlockingHashMap.<TKey, TValue> newInvalidCacheLoader());
|
||||
|
||||
// Normal concurrent hash map
|
||||
locks = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a new map.
|
||||
* @param <TKey> Type of the key
|
||||
* @param <TValue> Type of the value
|
||||
* @return The created map.
|
||||
*/
|
||||
public static <TKey, TValue> BlockingHashMap<TKey, TValue> create() {
|
||||
return new BlockingHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until a value has been associated with the given key, and then retrieves that value.
|
||||
* @param key - the key whose associated value is to be returned
|
||||
* @return The value to which the specified key is mapped.
|
||||
* @throws InterruptedException If the current thread got interrupted while waiting.
|
||||
*/
|
||||
public TValue get(TKey key) throws InterruptedException {
|
||||
if (key == null)
|
||||
throw new IllegalArgumentException("key cannot be NULL.");
|
||||
|
||||
TValue value = backingMap.get(key);
|
||||
|
||||
// Only lock if no value is available
|
||||
if (value == null) {
|
||||
final Object lock = getLock(key);
|
||||
|
||||
synchronized (lock) {
|
||||
while (value == null) {
|
||||
lock.wait();
|
||||
value = backingMap.get(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until a value has been associated with the given key, and then retrieves that value.
|
||||
* @param key - the key whose associated value is to be returned
|
||||
* @param timeout - the amount of time to wait until an association has been made.
|
||||
* @param unit - unit of timeout.
|
||||
* @return The value to which the specified key is mapped, or NULL if the timeout elapsed.
|
||||
* @throws InterruptedException If the current thread got interrupted while waiting.
|
||||
*/
|
||||
public TValue get(TKey key, long timeout, TimeUnit unit) throws InterruptedException {
|
||||
return get(key, timeout, unit, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until a value has been associated with the given key, and then retrieves that value.
|
||||
* <p>
|
||||
* If timeout is zero, this method will return immediately if it can't find an socket injector.
|
||||
*
|
||||
* @param key - the key whose associated value is to be returned
|
||||
* @param timeout - the amount of time to wait until an association has been made.
|
||||
* @param unit - unit of timeout.
|
||||
* @param ignoreInterrupted - TRUE if we should ignore the thread being interrupted, FALSE otherwise.
|
||||
* @return The value to which the specified key is mapped, or NULL if the timeout elapsed.
|
||||
* @throws InterruptedException If the current thread got interrupted while waiting.
|
||||
*/
|
||||
public TValue get(TKey key, long timeout, TimeUnit unit, boolean ignoreInterrupted) throws InterruptedException {
|
||||
if (key == null)
|
||||
throw new IllegalArgumentException("key cannot be NULL.");
|
||||
if (unit == null)
|
||||
throw new IllegalArgumentException("Unit cannot be NULL.");
|
||||
if (timeout < 0)
|
||||
throw new IllegalArgumentException("Timeout cannot be less than zero.");
|
||||
|
||||
TValue value = backingMap.get(key);
|
||||
|
||||
// Only lock if no value is available
|
||||
if (value == null && timeout > 0) {
|
||||
final Object lock = getLock(key);
|
||||
final long stopTimeNS = System.nanoTime() + unit.toNanos(timeout);
|
||||
|
||||
// Don't exceed the timeout
|
||||
synchronized (lock) {
|
||||
while (value == null) {
|
||||
try {
|
||||
long remainingTime = stopTimeNS - System.nanoTime();
|
||||
|
||||
if (remainingTime > 0) {
|
||||
TimeUnit.NANOSECONDS.timedWait(lock, remainingTime);
|
||||
value = backingMap.get(key);
|
||||
} else {
|
||||
// Timeout elapsed
|
||||
break;
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// This is fairly dangerous - but we might HAVE to block the thread
|
||||
if (!ignoreInterrupted)
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a given key with the given value.
|
||||
* <p>
|
||||
* Wakes up any blocking getters on this specific key.
|
||||
*
|
||||
* @param key - the key to associate.
|
||||
* @param value - the value.
|
||||
* @return The previously associated value.
|
||||
*/
|
||||
public TValue put(TKey key, TValue value) {
|
||||
if (value == null)
|
||||
throw new IllegalArgumentException("This map doesn't support NULL values.");
|
||||
|
||||
final TValue previous = backingMap.put(key, value);
|
||||
final Object lock = getLock(key);
|
||||
|
||||
// Inform our readers about this change
|
||||
synchronized (lock) {
|
||||
lock.notifyAll();
|
||||
return previous;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If and only if a key is not present in the map will it be associated with the given value.
|
||||
* @param key - the key to associate.
|
||||
* @param value - the value to associate.
|
||||
* @return The previous value this key has been associated with.
|
||||
*/
|
||||
public TValue putIfAbsent(TKey key, TValue value) {
|
||||
if (value == null)
|
||||
throw new IllegalArgumentException("This map doesn't support NULL values.");
|
||||
|
||||
final TValue previous = backingMap.putIfAbsent(key, value);
|
||||
|
||||
// No need to unlock readers if we haven't changed anything
|
||||
if (previous == null) {
|
||||
final Object lock = getLock(key);
|
||||
|
||||
synchronized (lock) {
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
return previous;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return backingMap.size();
|
||||
}
|
||||
|
||||
public Collection<TValue> values() {
|
||||
return backingMap.values();
|
||||
}
|
||||
|
||||
public Set<TKey> keys() {
|
||||
return backingMap.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically retrieve the lock associated with a given key.
|
||||
* @param key - the current key.
|
||||
* @return An asssociated lock.
|
||||
*/
|
||||
private Object getLock(TKey key) {
|
||||
Object lock = locks.get(key);
|
||||
|
||||
if (lock == null) {
|
||||
Object created = new Object();
|
||||
|
||||
// Do this atomically
|
||||
lock = locks.putIfAbsent(key, created);
|
||||
|
||||
// If we succeeded, use the latch we created - otherwise, use the already inserted latch
|
||||
if (lock == null) {
|
||||
lock = created;
|
||||
}
|
||||
}
|
||||
|
||||
return lock;
|
||||
}
|
||||
}
|
||||
@@ -31,18 +31,14 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.comphenix.protocol.ProtocolConfig;
|
||||
import com.comphenix.protocol.ProtocolLib;
|
||||
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.apache.commons.lang.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang.builder.ToStringStyle;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
@@ -473,18 +469,18 @@ 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) {
|
||||
// Apache is probably missing
|
||||
apacheCommonsMissing = true;
|
||||
} catch (ThreadDeath | OutOfMemoryError e) {
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} 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 {
|
||||
|
||||
@@ -1,18 +1,62 @@
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.utility.StreamSerializer;
|
||||
import com.comphenix.protocol.wrappers.*;
|
||||
import com.comphenix.protocol.wrappers.BlockPosition;
|
||||
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||
import com.comphenix.protocol.wrappers.ChunkCoordIntPair;
|
||||
import com.comphenix.protocol.wrappers.Converters;
|
||||
import com.comphenix.protocol.wrappers.CustomPacketPayloadWrapper;
|
||||
import com.comphenix.protocol.wrappers.Either;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers;
|
||||
import com.comphenix.protocol.wrappers.MinecraftKey;
|
||||
import com.comphenix.protocol.wrappers.MovingObjectPositionBlock;
|
||||
import com.comphenix.protocol.wrappers.MultiBlockChangeInfo;
|
||||
import com.comphenix.protocol.wrappers.Pair;
|
||||
import com.comphenix.protocol.wrappers.PlayerInfoData;
|
||||
import com.comphenix.protocol.wrappers.WrappedAttribute;
|
||||
import com.comphenix.protocol.wrappers.WrappedBlockData;
|
||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataValue;
|
||||
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
|
||||
import com.comphenix.protocol.wrappers.WrappedEnumEntityUseAction;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
import com.comphenix.protocol.wrappers.WrappedLevelChunkData;
|
||||
import com.comphenix.protocol.wrappers.WrappedMessageSignature;
|
||||
import com.comphenix.protocol.wrappers.WrappedNumberFormat;
|
||||
import com.comphenix.protocol.wrappers.WrappedParticle;
|
||||
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey;
|
||||
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
|
||||
import com.comphenix.protocol.wrappers.WrappedRegistrable;
|
||||
import com.comphenix.protocol.wrappers.WrappedRegistry;
|
||||
import com.comphenix.protocol.wrappers.WrappedRemoteChatSessionData;
|
||||
import com.comphenix.protocol.wrappers.WrappedSaltedSignature;
|
||||
import com.comphenix.protocol.wrappers.WrappedServerPing;
|
||||
import com.comphenix.protocol.wrappers.WrappedStatistic;
|
||||
import com.comphenix.protocol.wrappers.WrappedTeamParameters;
|
||||
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 org.apache.commons.lang.Validate;
|
||||
import com.comphenix.protocol.utility.Validate;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.World;
|
||||
@@ -25,11 +69,6 @@ import org.bukkit.potion.PotionEffectType;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.lang.reflect.Array;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
|
||||
public abstract class AbstractStructure {
|
||||
protected transient Object handle;
|
||||
protected transient StructureModifier<Object> structureModifier;
|
||||
@@ -1134,6 +1173,17 @@ public abstract class AbstractStructure {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a read/write structure for the ClientIntent enum used in Handshake/SetProtocol packet
|
||||
* @return A modifier for Protocol enum fields.
|
||||
*/
|
||||
public StructureModifier<EnumWrappers.ClientIntent> getClientIntents() {
|
||||
// Convert to and from the wrapper
|
||||
return structureModifier.withType(
|
||||
EnumWrappers.getClientIntentClass(),
|
||||
EnumWrappers.getClientIntentConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param leftConverter converter for left values
|
||||
* @param rightConverter converter for right values
|
||||
|
||||
@@ -13,6 +13,7 @@ public enum ListenerOptions {
|
||||
* Disable the automatic game phase detection that will normally force {@link GamePhase#LOGIN} when a packet ID is
|
||||
* known to be transmitted during login.
|
||||
*/
|
||||
@Deprecated
|
||||
DISABLE_GAMEPHASE_DETECTION,
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,7 +17,12 @@
|
||||
|
||||
package com.comphenix.protocol.events;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.injector.GamePhase;
|
||||
@@ -90,20 +95,6 @@ public class ListeningWhitelist {
|
||||
return new Builder(template);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a copy of a given enum.
|
||||
*
|
||||
* @param options - the options to copy, or NULL to indicate the empty set.
|
||||
* @return A copy of the enum set.
|
||||
*/
|
||||
private static <T extends Enum<T>> EnumSet<T> safeEnumSet(Collection<T> options, Class<T> enumClass) {
|
||||
if (options != null && !options.isEmpty()) {
|
||||
return EnumSet.copyOf(options);
|
||||
} else {
|
||||
return EnumSet.noneOf(enumClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a copy of a given set.
|
||||
*
|
||||
@@ -311,6 +302,7 @@ public class ListeningWhitelist {
|
||||
* @param gamePhase - the gamephase.
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@Deprecated
|
||||
public Builder gamePhase(GamePhase gamePhase) {
|
||||
this.gamePhase = gamePhase;
|
||||
return this;
|
||||
@@ -321,6 +313,7 @@ public class ListeningWhitelist {
|
||||
*
|
||||
* @return This builder, for chaining.
|
||||
*/
|
||||
@Deprecated
|
||||
public Builder gamePhaseBoth() {
|
||||
return gamePhase(GamePhase.BOTH);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -152,15 +152,33 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
|
||||
return lastSeen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getRespawnLocation() {
|
||||
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 {
|
||||
@@ -243,17 +261,10 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public PlayerProfile getPlayerProfile() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
public com.destroystokyo.paper.profile.PlayerProfile getPlayerProfile() {
|
||||
return null;
|
||||
}
|
||||
*/
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
@@ -297,7 +308,10 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
|
||||
return online;
|
||||
}
|
||||
|
||||
// @Override
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -22,6 +22,7 @@ package com.comphenix.protocol.injector;
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
@Deprecated
|
||||
public enum GamePhase {
|
||||
/**
|
||||
* Only listen for packets sent or received before a player has logged in.
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
+3
-15
@@ -25,7 +25,7 @@ import com.comphenix.protocol.events.PacketEvent;
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public interface ListenerInvoker {
|
||||
public interface ListenerManager {
|
||||
|
||||
boolean hasInboundListener(PacketType packetType);
|
||||
|
||||
@@ -38,24 +38,12 @@ public interface ListenerInvoker {
|
||||
*
|
||||
* @param event - the packet event to invoke.
|
||||
*/
|
||||
void invokePacketReceiving(PacketEvent event);
|
||||
void invokeInboundPacketListeners(PacketEvent event);
|
||||
|
||||
/**
|
||||
* Invokes the given packet event for every registered listener.
|
||||
*
|
||||
* @param event - the packet event to invoke.
|
||||
*/
|
||||
void invokePacketSending(PacketEvent event);
|
||||
|
||||
/**
|
||||
* Retrieve the associated type of a packet.
|
||||
*
|
||||
* @param packet - the packet.
|
||||
* @return The packet type.
|
||||
* @deprecated use
|
||||
* {@link com.comphenix.protocol.injector.packet.PacketRegistry#getPacketType(PacketType.Protocol, Class)}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
PacketType getPacketType(Object packet);
|
||||
void invokeOutboundPacketListeners(PacketEvent event);
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import java.util.Deque;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
@@ -7,7 +9,6 @@ import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.events.PacketPostListener;
|
||||
import com.comphenix.protocol.events.ScheduledPacket;
|
||||
import java.util.Deque;
|
||||
|
||||
/**
|
||||
* Represents a processor for network markers.
|
||||
@@ -43,7 +44,7 @@ public class NetworkProcessor {
|
||||
for (PacketPostListener listener : marker.getPostListeners()) {
|
||||
try {
|
||||
listener.onPostEvent(event);
|
||||
} catch (OutOfMemoryError | ThreadDeath e) {
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
this.reporter.reportMinimal(listener.getPlugin(), "SentListener.run()", e);
|
||||
|
||||
@@ -172,8 +172,6 @@ public class PacketConstructor {
|
||||
result = unwrapper.unwrapItem(values[i]);
|
||||
} catch (OutOfMemoryError e) {
|
||||
throw e;
|
||||
} catch (ThreadDeath e) {
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
lastException = e;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -46,15 +31,18 @@ import com.comphenix.protocol.injector.collection.PacketListenerSet;
|
||||
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.injector.player.PlayerInjectionHandler;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler.ConflictStrategy;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
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 ListenerInvoker, InternalManager {
|
||||
public class PacketFilterManager implements ListenerManager, InternalManager {
|
||||
|
||||
// plugin verifier reports
|
||||
private static final ReportType PLUGIN_VERIFIER_ERROR = new ReportType("Plugin verifier error: %s");
|
||||
@@ -80,7 +68,6 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
||||
private final Set<PacketListener> registeredListeners;
|
||||
|
||||
// injectors
|
||||
private final PlayerInjectionHandler playerInjectionHandler;
|
||||
private final NetworkManagerInjector networkManagerInjector;
|
||||
|
||||
// status of this manager
|
||||
@@ -111,10 +98,8 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
||||
// injectors
|
||||
this.networkManagerInjector = new NetworkManagerInjector(
|
||||
builder.getLibrary(),
|
||||
builder.getServer(),
|
||||
this,
|
||||
builder.getReporter());
|
||||
this.playerInjectionHandler = this.networkManagerInjector.getPlayerInjectionHandler();
|
||||
|
||||
// ensure that all packet types are loaded and synced
|
||||
PacketRegistry.getClientPacketTypes();
|
||||
@@ -122,7 +107,7 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
||||
|
||||
// hook into all connected players
|
||||
for (Player player : this.server.getOnlinePlayers()) {
|
||||
this.playerInjectionHandler.injectPlayer(player, ConflictStrategy.BAIL_OUT);
|
||||
this.networkManagerInjector.getInjector(player).inject();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +165,7 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
||||
}
|
||||
|
||||
// process outbound
|
||||
this.playerInjectionHandler.sendServerPacket(receiver, packet, marker, filters);
|
||||
this.networkManagerInjector.getInjector(receiver).sendClientboundPacket(packet.getHandle(), marker, filters);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,13 +177,7 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
||||
@Override
|
||||
public void sendWirePacket(Player receiver, WirePacket packet) {
|
||||
if (!this.closed) {
|
||||
// special case - we just throw the wire packet down the pipeline without processing it
|
||||
Channel outboundChannel = this.playerInjectionHandler.getChannel(receiver);
|
||||
if (outboundChannel == null) {
|
||||
throw new IllegalArgumentException("Unable to obtain connection of player " + receiver);
|
||||
}
|
||||
|
||||
outboundChannel.writeAndFlush(packet);
|
||||
this.networkManagerInjector.getInjector(receiver).sendWirePacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,7 +206,7 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
||||
if (filters) {
|
||||
// post to all listeners
|
||||
PacketEvent event = PacketEvent.fromClient(this.networkManagerInjector, packet, null, sender);
|
||||
this.invokePacketReceiving(event);
|
||||
this.invokeInboundPacketListeners(event);
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
@@ -240,13 +219,13 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
||||
}
|
||||
|
||||
// post to the player inject, reset our cancel state change
|
||||
this.playerInjectionHandler.receiveClientPacket(sender, nmsPacket);
|
||||
this.networkManagerInjector.getInjector(sender).readServerboundPacket(nmsPacket);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion(Player player) {
|
||||
return this.playerInjectionHandler.getProtocolVersion(player);
|
||||
return this.networkManagerInjector.getInjector(player).getProtocolVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -341,24 +320,30 @@ public class PacketFilterManager implements ListenerInvoker, 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);
|
||||
@@ -435,40 +420,26 @@ public class PacketFilterManager implements ListenerInvoker, 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) {
|
||||
PacketFilterManager.this.playerInjectionHandler.updatePlayer(event.getPlayer());
|
||||
}
|
||||
public void injectChannel(Channel channel) {
|
||||
networkManagerInjector.getInjector(channel).inject();
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void handleJoin(PlayerJoinEvent event) {
|
||||
PacketFilterManager.this.playerInjectionHandler.updatePlayer(event.getPlayer());
|
||||
}
|
||||
|
||||
@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
|
||||
@@ -518,33 +489,19 @@ public class PacketFilterManager implements ListenerInvoker, InternalManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokePacketReceiving(PacketEvent event) {
|
||||
public void invokeInboundPacketListeners(PacketEvent event) {
|
||||
if (!this.closed) {
|
||||
this.postPacketToListeners(this.inboundListeners, event, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokePacketSending(PacketEvent event) {
|
||||
public void invokeOutboundPacketListeners(PacketEvent event) {
|
||||
if (!this.closed) {
|
||||
this.postPacketToListeners(this.outboundListeners, event, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketType getPacketType(Object packet) {
|
||||
if (!MinecraftReflection.isPacketClass(packet)) {
|
||||
throw new IllegalArgumentException("Given packet is not a minecraft packet instance");
|
||||
}
|
||||
|
||||
PacketType type = PacketRegistry.getPacketType(packet.getClass());
|
||||
if (type != null) {
|
||||
return type;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unable to associate given packet " + packet + " with a registered packet!");
|
||||
}
|
||||
|
||||
private void postPacketToListeners(PacketListenerSet listeners, PacketEvent event, boolean outbound) {
|
||||
try {
|
||||
// append async marker if any async listener for the packet was registered
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.comphenix.protocol.injector.collection;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -45,7 +46,9 @@ public abstract class PacketListenerSet {
|
||||
public void addListener(PacketListener packetListener) {
|
||||
ListeningWhitelist listeningWhitelist = getListeningWhitelist(packetListener);
|
||||
|
||||
for (PacketType packetType : listeningWhitelist.getTypes()) {
|
||||
for (Iterator<PacketType> packet = listeningWhitelist.getTypes().iterator(); packet.hasNext();) {
|
||||
PacketType packetType = packet.next();
|
||||
|
||||
Set<PacketType> supportedPacketTypes = (packetType.getSender() == Sender.SERVER)
|
||||
? PacketRegistry.getServerPacketTypes()
|
||||
: PacketRegistry.getClientPacketTypes();
|
||||
@@ -56,7 +59,7 @@ public abstract class PacketListenerSet {
|
||||
.build());
|
||||
|
||||
// remove unknown packet types
|
||||
listeningWhitelist.getTypes().remove(packetType);
|
||||
packet.remove();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
|
||||
/**
|
||||
* Represents a listener for received or sent packets.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public interface ChannelListener {
|
||||
|
||||
/**
|
||||
* Invoked when a packet is being sent to the client.
|
||||
* <p>
|
||||
* This is invoked on the main thread.
|
||||
*
|
||||
* @param injector - the channel injector.
|
||||
* @param packet - the packet.
|
||||
* @param marker - the network marker.
|
||||
* @return The packet even that was passed to the listeners, with a possible packet change, or NULL.
|
||||
*/
|
||||
PacketEvent onPacketSending(Injector injector, PacketContainer packet, NetworkMarker marker);
|
||||
|
||||
/**
|
||||
* Invoked when a packet is being received from a client.
|
||||
* <p>
|
||||
* This is invoked on an asynchronous worker thread.
|
||||
*
|
||||
* @param injector - the channel injector.
|
||||
* @param packet - the packet.
|
||||
* @param marker - the associated network marker, if any.
|
||||
* @return The packet even that was passed to the listeners, with a possible packet change, or NULL.
|
||||
*/
|
||||
PacketEvent onPacketReceiving(Injector injector, PacketContainer packet, NetworkMarker marker);
|
||||
|
||||
boolean hasInboundListener(PacketType packetType);
|
||||
|
||||
boolean hasOutboundListener(PacketType packetType);
|
||||
|
||||
boolean hasMainThreadListener(PacketType type);
|
||||
|
||||
/**
|
||||
* Retrieve the current error reporter.
|
||||
*
|
||||
* @return The error reporter.
|
||||
*/
|
||||
ErrorReporter getReporter();
|
||||
|
||||
/**
|
||||
* Determine if debug mode is enabled.
|
||||
*
|
||||
* @return TRUE if it is, FALSE otherwise.
|
||||
*/
|
||||
boolean isDebug();
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
package com.comphenix.protocol.injector.netty;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
@@ -12,6 +15,8 @@ import org.bukkit.entity.Player;
|
||||
*/
|
||||
public interface Injector {
|
||||
|
||||
SocketAddress getAddress();
|
||||
|
||||
/**
|
||||
* Retrieve the current protocol version of the player.
|
||||
*
|
||||
@@ -23,12 +28,8 @@ public interface Injector {
|
||||
* Inject the current channel.
|
||||
* <p>
|
||||
* Note that only active channels can be injected.
|
||||
*
|
||||
* @return TRUE if we injected the channel, false if we could not inject or it was already injected.
|
||||
*/
|
||||
boolean inject();
|
||||
|
||||
void uninject();
|
||||
void inject();
|
||||
|
||||
/**
|
||||
* Close the current injector.
|
||||
@@ -42,20 +43,13 @@ public interface Injector {
|
||||
* @param marker - the network marker.
|
||||
* @param filtered - whether or not the packet is filtered.
|
||||
*/
|
||||
void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered);
|
||||
void sendClientboundPacket(Object packet, NetworkMarker marker, boolean filtered);
|
||||
|
||||
void receiveClientPacket(Object packet);
|
||||
void readServerboundPacket(Object packet);
|
||||
|
||||
/**
|
||||
* Retrieve the current protocol state.
|
||||
*
|
||||
* @return The current protocol.
|
||||
* @deprecated use {@link #getCurrentProtocol(PacketType.Sender)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
default Protocol getCurrentProtocol() {
|
||||
return this.getCurrentProtocol(PacketType.Sender.SERVER);
|
||||
}
|
||||
void sendWirePacket(WirePacket packet);
|
||||
|
||||
void disconnect(String message);
|
||||
|
||||
/**
|
||||
* Retrieve the current protocol state. Note that since 1.20.2 the client and server direction can be in different
|
||||
@@ -66,22 +60,6 @@ public interface Injector {
|
||||
*/
|
||||
Protocol getCurrentProtocol(PacketType.Sender sender);
|
||||
|
||||
/**
|
||||
* Retrieve the network marker associated with a given packet.
|
||||
*
|
||||
* @param packet - the packet.
|
||||
* @return The network marker.
|
||||
*/
|
||||
NetworkMarker getMarker(Object packet);
|
||||
|
||||
/**
|
||||
* Associate a given network marker with a specific packet.
|
||||
*
|
||||
* @param packet - the NMS packet.
|
||||
* @param marker - the associated marker.
|
||||
*/
|
||||
void saveMarker(Object packet, NetworkMarker marker);
|
||||
|
||||
/**
|
||||
* Retrieve the current player or temporary player associated with the injector.
|
||||
*
|
||||
@@ -96,7 +74,9 @@ public interface Injector {
|
||||
*/
|
||||
void setPlayer(Player player);
|
||||
|
||||
void disconnect(String message);
|
||||
boolean hasValidPlayer();
|
||||
|
||||
boolean isConnected();
|
||||
|
||||
/**
|
||||
* Determine if the channel has already been injected.
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
package com.comphenix.protocol.injector.netty.channel;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
|
||||
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 {
|
||||
@@ -16,18 +21,21 @@ final class EmptyInjector implements Injector {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getAddress() {
|
||||
if (this.player != null) {
|
||||
return this.player.getAddress();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion() {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inject() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uninject() {
|
||||
public void inject() {
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -35,11 +43,19 @@ final class EmptyInjector implements Injector {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) {
|
||||
public void sendClientboundPacket(Object packet, NetworkMarker marker, boolean filtered) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveClientPacket(Object packet) {
|
||||
public void readServerboundPacket(Object packet) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendWirePacket(WirePacket packet) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(String message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -47,15 +63,6 @@ final class EmptyInjector implements Injector {
|
||||
return Protocol.HANDSHAKING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NetworkMarker getMarker(Object packet) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveMarker(Object packet, NetworkMarker marker) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
return this.player;
|
||||
@@ -67,7 +74,13 @@ final class EmptyInjector implements Injector {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(String message) {
|
||||
public boolean hasValidPlayer() {
|
||||
return player != null && !(player instanceof TemporaryPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+14
-14
@@ -2,7 +2,8 @@ package com.comphenix.protocol.injector.netty.channel;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLogger;
|
||||
import com.comphenix.protocol.injector.netty.ChannelListener;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
|
||||
@@ -12,39 +13,38 @@ import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
final class InboundPacketInterceptor extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private final NettyChannelInjector injector;
|
||||
private final ChannelListener channelListener;
|
||||
|
||||
public InboundPacketInterceptor(NettyChannelInjector injector, ChannelListener listener) {
|
||||
public InboundPacketInterceptor(NettyChannelInjector injector) {
|
||||
this.injector = injector;
|
||||
this.channelListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) {
|
||||
if (MinecraftReflection.isPacketClass(msg)) {
|
||||
// process the login if the packet is one before posting the packet to any
|
||||
// handler to provide "real" data the method invocation will do nothing if the
|
||||
// packet is not a login packet
|
||||
this.injector.tryProcessLogin(msg);
|
||||
|
||||
// try get packet type
|
||||
PacketType.Protocol protocol = this.injector.getInboundProtocol();
|
||||
PacketType packetType = PacketRegistry.getPacketType(protocol, msg.getClass());
|
||||
if (protocol == Protocol.UNKNOWN) {
|
||||
ProtocolLogger.debug("skipping unknown inbound protocol for {0}", msg.getClass());
|
||||
ctx.fireChannelRead(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
PacketType packetType = PacketRegistry.getPacketType(protocol, msg.getClass());
|
||||
if (packetType == null) {
|
||||
ProtocolLogger.debug("skipping unknown inbound packet type for {0}", msg.getClass());
|
||||
ctx.fireChannelRead(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// check if there are any listeners bound for the packet - if not just post the
|
||||
// check if there are any listeners bound for the packet - if not just send the
|
||||
// packet down the pipeline
|
||||
if (!this.channelListener.hasInboundListener(packetType)) {
|
||||
if (!this.injector.hasInboundListener(packetType)) {
|
||||
ctx.fireChannelRead(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// call all inbound listeners
|
||||
this.injector.processInboundPacket(ctx, msg, packetType);
|
||||
// don't invoke next handler and transfer packet to injector for further processing
|
||||
this.injector.processInbound(ctx, new PacketContainer(packetType, msg));
|
||||
} else {
|
||||
// just pass the message down the pipeline
|
||||
ctx.fireChannelRead(msg);
|
||||
|
||||
@@ -14,19 +14,22 @@
|
||||
*/
|
||||
package com.comphenix.protocol.injector.netty.channel;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLogger;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.injector.netty.ChannelListener;
|
||||
import com.comphenix.protocol.injector.ListenerManager;
|
||||
import com.comphenix.protocol.injector.netty.Injector;
|
||||
import com.comphenix.protocol.injector.temporary.MinimalInjector;
|
||||
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 io.netty.channel.Channel;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import javax.annotation.Nonnull;
|
||||
import org.bukkit.Server;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
@@ -39,24 +42,23 @@ import org.bukkit.plugin.Plugin;
|
||||
*/
|
||||
public class InjectionFactory {
|
||||
|
||||
// This should work as long as the injectors are, uh, injected
|
||||
private final ConcurrentMap<String, Injector> nameLookup = new MapMaker().weakValues().makeMap();
|
||||
private final ConcurrentMap<Player, Injector> playerLookup = new MapMaker().weakKeys().weakValues().makeMap();
|
||||
|
||||
// bukkit stuff
|
||||
private final Plugin plugin;
|
||||
private final Server server;
|
||||
|
||||
// protocol lib stuff
|
||||
private final ErrorReporter errorReporter;
|
||||
private final ListenerManager listenerManager;
|
||||
|
||||
// state of the factory
|
||||
private boolean closed;
|
||||
|
||||
public InjectionFactory(Plugin plugin, Server server, ErrorReporter errorReporter) {
|
||||
public InjectionFactory(Plugin plugin, ErrorReporter errorReporter, ListenerManager listenerManager) {
|
||||
this.plugin = plugin;
|
||||
this.server = server;
|
||||
this.errorReporter = errorReporter;
|
||||
this.listenerManager = listenerManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,23 +74,18 @@ public class InjectionFactory {
|
||||
* Construct or retrieve a channel injector from an existing Bukkit player.
|
||||
*
|
||||
* @param player - the existing Bukkit player.
|
||||
* @param listener - the listener.
|
||||
* @return A new injector, an existing injector associated with this player, or a closed injector.
|
||||
*/
|
||||
@Nonnull
|
||||
public Injector fromPlayer(Player player, ChannelListener listener) {
|
||||
public Injector fromPlayer(Player player) {
|
||||
if (this.closed) {
|
||||
return new EmptyInjector(player);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -108,16 +105,18 @@ 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
|
||||
injector = new NettyChannelInjector(
|
||||
player,
|
||||
this.server,
|
||||
networkManager,
|
||||
channel,
|
||||
listener,
|
||||
this.listenerManager,
|
||||
this,
|
||||
this.errorReporter);
|
||||
this.cacheInjector(player, injector);
|
||||
@@ -155,31 +154,27 @@ public class InjectionFactory {
|
||||
* Construct a new channel injector for the given channel.
|
||||
*
|
||||
* @param channel - the channel.
|
||||
* @param listener - the listener.
|
||||
* @param playerFactory - a temporary player creator.
|
||||
* @return The channel injector, or a closed injector.
|
||||
*/
|
||||
@Nonnull
|
||||
public Injector fromChannel(Channel channel, ChannelListener listener, TemporaryPlayerFactory playerFactory) {
|
||||
public Injector fromChannel(Channel channel) {
|
||||
if (this.closed) {
|
||||
return EmptyInjector.WITHOUT_PLAYER;
|
||||
}
|
||||
|
||||
Object netManager = this.findNetworkManager(channel);
|
||||
Player temporaryPlayer = playerFactory.createTemporaryPlayer(this.server);
|
||||
Object networkManager = this.findNetworkManager(channel);
|
||||
Player temporaryPlayer = TemporaryPlayerFactory.createTemporaryPlayer();
|
||||
|
||||
NettyChannelInjector injector = new NettyChannelInjector(
|
||||
temporaryPlayer,
|
||||
this.server,
|
||||
netManager,
|
||||
networkManager,
|
||||
channel,
|
||||
listener,
|
||||
this.listenerManager,
|
||||
this,
|
||||
this.errorReporter);
|
||||
MinimalInjector minimalInjector = new NettyChannelMinimalInjector(injector);
|
||||
|
||||
// Initialize temporary player
|
||||
TemporaryPlayerFactory.setInjectorInPlayer(temporaryPlayer, minimalInjector);
|
||||
TemporaryPlayerFactory.setInjectorForPlayer(temporaryPlayer, injector);
|
||||
return injector;
|
||||
}
|
||||
|
||||
@@ -235,9 +230,9 @@ public class InjectionFactory {
|
||||
* @return The associated injector, or NULL if this is a Bukkit player.
|
||||
*/
|
||||
private NettyChannelInjector getTemporaryInjector(Player player) {
|
||||
MinimalInjector injector = TemporaryPlayerFactory.getInjectorFromPlayer(player);
|
||||
if (injector instanceof NettyChannelMinimalInjector) {
|
||||
return ((NettyChannelMinimalInjector) injector).getInjector();
|
||||
Injector injector = TemporaryPlayerFactory.getInjectorFromPlayer(player);
|
||||
if (injector instanceof NettyChannelInjector) {
|
||||
return (NettyChannelInjector) injector;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -250,11 +245,12 @@ public class InjectionFactory {
|
||||
* @return The network manager.
|
||||
*/
|
||||
private Object findNetworkManager(Channel channel) {
|
||||
// Find the network manager
|
||||
Object networkManager = NettyChannelInjector.findChannelHandler(channel,
|
||||
MinecraftReflection.getNetworkManagerClass());
|
||||
if (networkManager != null) {
|
||||
return networkManager;
|
||||
Class<?> networkManagerClass = MinecraftReflection.getNetworkManagerClass();
|
||||
|
||||
for (Entry<String, ChannelHandler> entry : channel.pipeline()) {
|
||||
if (networkManagerClass.isAssignableFrom(entry.getValue().getClass())) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unable to find NetworkManager in " + channel);
|
||||
|
||||
+275
-364
@@ -2,14 +2,14 @@ package com.comphenix.protocol.injector.netty.channel;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
@@ -21,86 +21,70 @@ import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.ListenerManager;
|
||||
import com.comphenix.protocol.injector.NetworkProcessor;
|
||||
import com.comphenix.protocol.injector.netty.ChannelListener;
|
||||
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;
|
||||
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
|
||||
import com.comphenix.protocol.utility.ByteBuddyGenerated;
|
||||
import com.comphenix.protocol.utility.MinecraftFields;
|
||||
import com.comphenix.protocol.utility.MinecraftMethods;
|
||||
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.util.AttributeKey;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class NettyChannelInjector implements Injector {
|
||||
|
||||
// an accessor used when we're unable to retrieve the actual packet field in an outbound packet send
|
||||
private static final FieldAccessor NO_OP_ACCESSOR = new FieldAccessor() {
|
||||
@Override
|
||||
public Object get(Object instance) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Object instance, Object value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Field getField() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
private static final String INTERCEPTOR_NAME = "protocol_lib_inbound_interceptor";
|
||||
private static final String PROTOCOL_READER_NAME = "protocol_lib_protocol_reader";
|
||||
private static final String INBOUND_INTERCEPTOR_NAME = "protocol_lib_inbound_interceptor";
|
||||
private static final String INBOUND_PROTOCOL_GETTER_NAME = "protocol_lib_inbound_protocol_getter";
|
||||
private static final String WIRE_PACKET_ENCODER_NAME = "protocol_lib_wire_packet_encoder";
|
||||
|
||||
// all registered channel handlers to easier make sure we unregister them all from the pipeline
|
||||
private static final String[] PROTOCOL_LIB_HANDLERS = new String[]{
|
||||
WIRE_PACKET_ENCODER_NAME, INTERCEPTOR_NAME, PROTOCOL_READER_NAME
|
||||
private static final String[] NETTY_HANDLER_NAMES = new String[]{
|
||||
WIRE_PACKET_ENCODER_NAME, INBOUND_INTERCEPTOR_NAME, INBOUND_PROTOCOL_GETTER_NAME
|
||||
};
|
||||
|
||||
private static final ReportType REPORT_CANNOT_SEND_PACKET = new ReportType("Unable to send packet %s to %s");
|
||||
private static final ReportType REPORT_CANNOT_SEND_WRITE_PACKET = new ReportType("Unable to send wire packet %s to %s");
|
||||
private static final ReportType REPORT_CANNOT_READ_PACKET = new ReportType("Unable to read packet %s for %s");
|
||||
private static final ReportType REPORT_CANNOT_DISCONNECT = new ReportType("Unable to disconnect %s for %s");
|
||||
|
||||
private static final WirePacketEncoder WIRE_PACKET_ENCODER = new WirePacketEncoder();
|
||||
private static final Map<Class<?>, FieldAccessor> PACKET_ACCESSORS = new ConcurrentHashMap<>(16, 0.9f);
|
||||
|
||||
private static final Class<?> LOGIN_PACKET_START_CLASS = PacketType.Login.Client.START.getPacketClass();
|
||||
private static final Class<?> PACKET_PROTOCOL_CLASS = PacketType.Handshake.Client.SET_PROTOCOL.getPacketClass();
|
||||
|
||||
// use random attribute name because they need to be unique and would throw on reload
|
||||
private static final AttributeKey<Integer> PROTOCOL_VERSION = AttributeKey.valueOf(getRandomKey());
|
||||
private static final AttributeKey<NettyChannelInjector> INJECTOR = AttributeKey.valueOf(getRandomKey());
|
||||
|
||||
// lazy initialized fields, if we don't need them we don't bother about them
|
||||
private static FieldAccessor LOGIN_PROFILE_ACCESSOR;
|
||||
private static FieldAccessor PROTOCOL_VERSION_ACCESSOR;
|
||||
static NettyChannelInjector findInjector(Channel channel) {
|
||||
return channel.attr(INJECTOR).get();
|
||||
}
|
||||
|
||||
// bukkit stuff
|
||||
private final Server server;
|
||||
private static String getRandomKey() {
|
||||
return "ProtocolLib-" + ThreadLocalRandom.current().nextLong();
|
||||
}
|
||||
|
||||
// protocol lib stuff we need
|
||||
private final ErrorReporter errorReporter;
|
||||
private final InjectionFactory injectionFactory;
|
||||
private final ListenerManager listenerManager;
|
||||
private final NetworkProcessor networkProcessor;
|
||||
private final PacketListenerInvoker listenerInvoker;
|
||||
|
||||
// references
|
||||
private final Object networkManager;
|
||||
private final Channel wrappedChannel;
|
||||
private final ChannelListener channelListener;
|
||||
private final InjectionFactory injectionFactory;
|
||||
private final Channel channel;
|
||||
|
||||
private final FieldAccessor channelField;
|
||||
|
||||
@@ -110,47 +94,44 @@ public class NettyChannelInjector implements Injector {
|
||||
protected final ThreadLocal<Boolean> processedPackets = ThreadLocal.withInitial(() -> Boolean.FALSE);
|
||||
|
||||
// status of this injector
|
||||
private volatile boolean closed = false;
|
||||
private final AtomicBoolean closed = new AtomicBoolean(false);
|
||||
private volatile boolean injected = false;
|
||||
|
||||
// information about the player belonging to this injector
|
||||
private String playerName;
|
||||
private Player resolvedPlayer;
|
||||
private Player player;
|
||||
|
||||
// lazy initialized fields, if we don't need them we don't bother about them
|
||||
private Object playerConnection;
|
||||
|
||||
private InboundProtocolReader inboundProtocolReader;
|
||||
private volatile InboundProtocolReader inboundProtocolReader;
|
||||
|
||||
public NettyChannelInjector(
|
||||
Player player,
|
||||
Server server,
|
||||
Object netManager,
|
||||
Object networkManager,
|
||||
Channel channel,
|
||||
ChannelListener listener,
|
||||
ListenerManager listenerManager,
|
||||
InjectionFactory injector,
|
||||
ErrorReporter errorReporter
|
||||
) {
|
||||
// bukkit stuff
|
||||
this.server = server;
|
||||
this.resolvedPlayer = player;
|
||||
this.player = player;
|
||||
|
||||
// protocol lib stuff
|
||||
this.errorReporter = errorReporter;
|
||||
this.networkProcessor = new NetworkProcessor(errorReporter);
|
||||
this.listenerInvoker = new PacketListenerInvoker(networkManager);
|
||||
|
||||
// references
|
||||
this.networkManager = netManager;
|
||||
this.wrappedChannel = channel;
|
||||
this.channelListener = listener;
|
||||
this.networkManager = networkManager;
|
||||
this.channel = channel;
|
||||
this.listenerManager = listenerManager;
|
||||
this.injectionFactory = injector;
|
||||
|
||||
// register us into the channel
|
||||
this.wrappedChannel.attr(INJECTOR).set(this);
|
||||
this.channel.attr(INJECTOR).set(this);
|
||||
|
||||
// read the channel field from the network manager given to this method
|
||||
// we re-read this field every time as plugins/spigot forks might give us different network manager types
|
||||
Field channelField = FuzzyReflection.fromObject(netManager, true).getField(FuzzyFieldContract.newBuilder()
|
||||
Field channelField = FuzzyReflection.fromObject(networkManager, true).getField(FuzzyFieldContract.newBuilder()
|
||||
.typeExact(Channel.class)
|
||||
.banModifier(Modifier.STATIC)
|
||||
.build());
|
||||
@@ -159,135 +140,110 @@ public class NettyChannelInjector implements Injector {
|
||||
// hook here into the close future to be 100% sure that this injector gets closed when the channel we wrap gets closed
|
||||
// normally we listen to the disconnect event, but there is a very small period of time, between the login and actual
|
||||
// join that is not covered by the disconnect event and may lead to unexpected injector states...
|
||||
this.wrappedChannel.closeFuture().addListener(future -> this.close());
|
||||
this.channel.closeFuture().addListener(future -> this.close());
|
||||
}
|
||||
|
||||
static NettyChannelInjector findInjector(Channel channel) {
|
||||
return channel.attr(INJECTOR).get();
|
||||
}
|
||||
|
||||
static Object findChannelHandler(Channel channel, Class<?> type) {
|
||||
for (Entry<String, ChannelHandler> entry : channel.pipeline()) {
|
||||
if (type.isAssignableFrom(entry.getValue().getClass())) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String getRandomKey() {
|
||||
return "ProtocolLib-" + ThreadLocalRandom.current().nextLong();
|
||||
}
|
||||
|
||||
private static boolean hasProtocolLibHandler(Channel channel) {
|
||||
for (String handler : PROTOCOL_LIB_HANDLERS) {
|
||||
if (channel.pipeline().get(handler) != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@Override
|
||||
public SocketAddress getAddress() {
|
||||
return this.channel.remoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion() {
|
||||
Integer protocolVersion = this.wrappedChannel.attr(PROTOCOL_VERSION).get();
|
||||
Integer protocolVersion = this.channel.attr(PROTOCOL_VERSION).get();
|
||||
return protocolVersion == null ? MinecraftProtocolVersion.getCurrentVersion() : protocolVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inject() {
|
||||
// we only do this on the channel event loop to prevent blocking the main server thread
|
||||
// and to be sure that the netty pipeline view we get is up-to-date
|
||||
if (this.wrappedChannel.eventLoop().inEventLoop()) {
|
||||
// ensure that we should actually inject into the channel
|
||||
if (this.closed || this.wrappedChannel instanceof ByteBuddyGenerated || !this.wrappedChannel.isActive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check here if we need to rewrite the channel field and do so
|
||||
// minecraft overrides the channel field when the channel actually becomes active, so we need to ensure that our
|
||||
// proxied channel is always on that field - therefore this rewrite is event before we check if we're already
|
||||
// injected into the channel
|
||||
this.rewriteChannelField();
|
||||
|
||||
// check if we already injected into the channel
|
||||
if (hasProtocolLibHandler(this.wrappedChannel)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ChannelPipeline pipeline = this.wrappedChannel.pipeline();
|
||||
|
||||
// since 1.20.5 the encoder is renamed to outbound_config when the channel is
|
||||
// waiting for the next protocol phase
|
||||
String encoderName = pipeline.get("outbound_config") != null
|
||||
? "outbound_config" : "encoder";
|
||||
|
||||
// inject our handlers
|
||||
pipeline.addAfter(
|
||||
encoderName,
|
||||
WIRE_PACKET_ENCODER_NAME,
|
||||
WIRE_PACKET_ENCODER);
|
||||
|
||||
// since 1.20.2 the packet decoder will remove or reconfigure the protocol for
|
||||
// terminal packets which is why we need to read it before the packet gets
|
||||
// decoded
|
||||
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
this.inboundProtocolReader = new InboundProtocolReader(this);
|
||||
pipeline.addBefore(
|
||||
"decoder",
|
||||
PROTOCOL_READER_NAME,
|
||||
this.inboundProtocolReader);
|
||||
}
|
||||
|
||||
// inject inbound packet interceptor
|
||||
pipeline.addAfter(
|
||||
"decoder",
|
||||
INTERCEPTOR_NAME,
|
||||
new InboundPacketInterceptor(this, this.channelListener));
|
||||
|
||||
this.injected = true;
|
||||
return true;
|
||||
} else {
|
||||
// re-run in event loop, return false as we cannot be sure if the injection actually worked
|
||||
this.ensureInEventLoop(this::inject);
|
||||
return false;
|
||||
public void inject() {
|
||||
// ensure we are in the channel's event loop to be "thread-safe" within netty
|
||||
// and our own code
|
||||
if (!this.channel.eventLoop().inEventLoop()) {
|
||||
channel.eventLoop().execute(this::inject);
|
||||
return;
|
||||
}
|
||||
|
||||
// don't need to inject if injector or channel got closed
|
||||
if (this.closed.get() || !this.channel.isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// wrap channel inside the NetworkMananger to proxy write calls to our
|
||||
// processOutbound method, this way we try to minimize the amount of thread
|
||||
// jumps from event loop to minecraft main thread in order to process packets in
|
||||
// main thread
|
||||
Object networkManangerChannel = this.channelField.get(this.networkManager);
|
||||
if (!(networkManangerChannel instanceof NettyChannelProxy)) {
|
||||
EventLoop proxyEventLoop = new NettyEventLoopProxy(this.channel.eventLoop(), this);
|
||||
Channel proxyChannel = new NettyChannelProxy(this.channel, proxyEventLoop, this);
|
||||
this.channelField.set(this.networkManager, proxyChannel);
|
||||
}
|
||||
|
||||
ChannelPipeline pipeline = this.channel.pipeline();
|
||||
|
||||
// since 1.20.5 the en-/decoder is renamed to out-/inbound_config when the
|
||||
// channel is waiting for the next protocol phase (after terminal packet)
|
||||
String encoderName = pipeline.get("outbound_config") != null
|
||||
? "outbound_config"
|
||||
: "encoder";
|
||||
String decoderName = pipeline.get("inbound_config") != null
|
||||
? "inbound_config"
|
||||
: "decoder";
|
||||
|
||||
// try to add wire packet encoder
|
||||
if (pipeline.context(WIRE_PACKET_ENCODER_NAME) == null) {
|
||||
pipeline.addAfter(encoderName, WIRE_PACKET_ENCODER_NAME, WIRE_PACKET_ENCODER);
|
||||
}
|
||||
|
||||
// try to add protocol reader, this is necessary because the en-/decoder will
|
||||
// remove or reconfigure the protocol for terminal since 1.20.2+
|
||||
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove() && pipeline.context(INBOUND_PROTOCOL_GETTER_NAME) == null) {
|
||||
this.inboundProtocolReader = new InboundProtocolReader(this);
|
||||
pipeline.addBefore(decoderName, INBOUND_PROTOCOL_GETTER_NAME, this.inboundProtocolReader);
|
||||
}
|
||||
|
||||
// try to add inbound packet interceptor
|
||||
if (pipeline.context(INBOUND_INTERCEPTOR_NAME) == null) {
|
||||
pipeline.addAfter(decoderName, INBOUND_INTERCEPTOR_NAME, new InboundPacketInterceptor(this));
|
||||
}
|
||||
|
||||
// mark injector as injected
|
||||
this.injected = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uninject() {
|
||||
// ensure that we injected into the channel before trying to remove anything from it
|
||||
if (this.injected) {
|
||||
// uninject on the event loop to ensure the instant visibility of the change and prevent blocks of other threads
|
||||
if (this.wrappedChannel.eventLoop().inEventLoop()) {
|
||||
this.injected = false;
|
||||
private void uninject() {
|
||||
// ensure we are in the channel's event loop to be "thread-safe" within netty
|
||||
// and our own code
|
||||
if (!this.channel.eventLoop().inEventLoop()) {
|
||||
channel.eventLoop().execute(this::uninject);
|
||||
return;
|
||||
}
|
||||
|
||||
// remove known references to us
|
||||
this.wrappedChannel.attr(INJECTOR).remove();
|
||||
this.channelField.set(this.networkManager, this.wrappedChannel);
|
||||
// replace wrapped channel in NetworkManager with original channel
|
||||
this.channelField.set(this.networkManager, this.channel);
|
||||
|
||||
for (String handler : PROTOCOL_LIB_HANDLERS) {
|
||||
try {
|
||||
this.wrappedChannel.pipeline().remove(handler);
|
||||
} catch (NoSuchElementException ignored) {
|
||||
// ignore that one, probably an edge case
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.ensureInEventLoop(this::uninject);
|
||||
// remove all ProtocolLib netty handler
|
||||
ChannelPipeline pipeline = this.channel.pipeline();
|
||||
for (String handlerName : NETTY_HANDLER_NAMES) {
|
||||
if (pipeline.context(handlerName) != null) {
|
||||
pipeline.remove(handlerName);
|
||||
}
|
||||
}
|
||||
|
||||
// mark injector as uninjected
|
||||
this.injected = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// ensure that the injector wasn't close before
|
||||
if (!this.closed) {
|
||||
this.closed = true;
|
||||
|
||||
if (this.closed.compareAndSet(false, true)) {
|
||||
// remove all of our references from the channel
|
||||
this.uninject();
|
||||
|
||||
// remove any outgoing references
|
||||
this.channel.attr(INJECTOR).remove();
|
||||
|
||||
// cleanup
|
||||
this.savedMarkers.clear();
|
||||
this.skippedPackets.clear();
|
||||
@@ -298,9 +254,9 @@ public class NettyChannelInjector implements Injector {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) {
|
||||
// do not send the packet if this injector was already closed / is not injected yet
|
||||
if (this.closed || !this.injected) {
|
||||
public void sendClientboundPacket(Object packet, NetworkMarker marker, boolean filtered) {
|
||||
// ignore call if the injector is closed or not injected
|
||||
if (this.closed.get() || !this.injected) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -310,19 +266,12 @@ public class NettyChannelInjector implements Injector {
|
||||
}
|
||||
|
||||
// save the given packet marker and send the packet
|
||||
this.saveMarker(packet, marker);
|
||||
if (marker != null) {
|
||||
this.savedMarkers.put(packet, marker);
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.resolvedPlayer instanceof ByteBuddyGenerated) {
|
||||
MinecraftMethods.getNetworkManagerHandleMethod().invoke(this.networkManager, packet);
|
||||
} else {
|
||||
// ensure that the player is properly connected before sending
|
||||
Object playerConnection = this.getPlayerConnection();
|
||||
if (playerConnection != null) {
|
||||
MinecraftMethods.getSendPacketMethod().invoke(playerConnection, packet);
|
||||
} else {
|
||||
MinecraftMethods.getNetworkManagerHandleMethod().invoke(this.networkManager, packet);
|
||||
}
|
||||
}
|
||||
this.listenerInvoker.send(packet);
|
||||
} catch (Exception exception) {
|
||||
this.errorReporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_SEND_PACKET)
|
||||
.messageParam(packet, this.playerName)
|
||||
@@ -332,89 +281,103 @@ public class NettyChannelInjector implements Injector {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveClientPacket(Object packet) {
|
||||
// do not do that if we're not injected or this injector was closed
|
||||
if (this.closed || !this.injected) {
|
||||
public void readServerboundPacket(Object packet) {
|
||||
// ignore call if the injector is closed or not injected
|
||||
if (this.closed.get() || !this.injected) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.ensureInEventLoop(() -> {
|
||||
try {
|
||||
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)
|
||||
.error(exception)
|
||||
.build());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendWirePacket(WirePacket packet) {
|
||||
// ignore call if the injector is closed or not injected
|
||||
if (this.closed.get() || !this.injected) {
|
||||
return;
|
||||
}
|
||||
|
||||
Runnable receiveAction = () -> {
|
||||
this.ensureInEventLoop(() -> {
|
||||
try {
|
||||
// try to invoke the method, this should normally not fail
|
||||
MinecraftMethods.getNetworkManagerReadPacketMethod().invoke(this.networkManager, null, packet);
|
||||
this.channel.writeAndFlush(packet);
|
||||
} catch (Exception exception) {
|
||||
// 99% the user gave wrong information to the server
|
||||
this.errorReporter.reportMinimal(this.injectionFactory.getPlugin(), "receiveClientPacket", exception);
|
||||
this.errorReporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_SEND_WRITE_PACKET)
|
||||
.messageParam(packet, this.playerName)
|
||||
.error(exception)
|
||||
.build());
|
||||
}
|
||||
};
|
||||
|
||||
// execute the action on the event loop rather than any thread which we should potentially not block
|
||||
if (this.wrappedChannel.eventLoop().inEventLoop()) {
|
||||
receiveAction.run();
|
||||
} else {
|
||||
this.ensureInEventLoop(receiveAction);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public PacketType.Protocol getInboundProtocol() {
|
||||
if (this.inboundProtocolReader != null) {
|
||||
return this.inboundProtocolReader.getProtocol();
|
||||
@Override
|
||||
public void disconnect(String message) {
|
||||
// ignore call if the injector is closed or not injected
|
||||
if (this.closed.get() || !this.injected) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.listenerInvoker.disconnect(message);
|
||||
} catch (Exception exception) {
|
||||
this.errorReporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_DISCONNECT)
|
||||
.messageParam(this.playerName, message)
|
||||
.error(exception)
|
||||
.build());
|
||||
}
|
||||
return getCurrentProtocol(PacketType.Sender.CLIENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Protocol getCurrentProtocol(PacketType.Sender sender) {
|
||||
return ChannelProtocolUtil.PROTOCOL_RESOLVER.apply(this.wrappedChannel, sender);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NetworkMarker getMarker(Object packet) {
|
||||
return this.savedMarkers.get(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveMarker(Object packet, NetworkMarker marker) {
|
||||
if (marker != null && !this.closed) {
|
||||
this.savedMarkers.put(packet, marker);
|
||||
}
|
||||
return ChannelProtocolUtil.PROTOCOL_RESOLVER.apply(this.channel, sender);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
// if the player was already resolved there is no need to do further lookups
|
||||
if (this.resolvedPlayer != null) {
|
||||
return this.resolvedPlayer;
|
||||
if (this.player != null && !(this.player instanceof TemporaryPlayer)) {
|
||||
return this.player;
|
||||
}
|
||||
|
||||
// check if the name of the player is already known to the injector
|
||||
if (this.playerName != null) {
|
||||
this.resolvedPlayer = this.server.getPlayerExact(this.playerName);
|
||||
this.player = Bukkit.getPlayerExact(this.playerName);
|
||||
if (this.player != null) {
|
||||
this.injectionFactory.cacheInjector(this.player, this);
|
||||
}
|
||||
}
|
||||
|
||||
// either we resolved it or we didn't...
|
||||
return this.resolvedPlayer;
|
||||
return this.player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayer(Player player) {
|
||||
this.resolvedPlayer = player;
|
||||
this.injectionFactory.invalidate(this.player, this.playerName);
|
||||
|
||||
this.player = player;
|
||||
this.playerName = player.getName();
|
||||
|
||||
this.injectionFactory.cacheInjector(player, this);
|
||||
this.injectionFactory.cacheInjector(player.getName(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(String message) {
|
||||
// we're still during pre-login, just close the connection
|
||||
if (this.playerConnection == null || this.resolvedPlayer instanceof ByteBuddyGenerated) {
|
||||
this.wrappedChannel.disconnect();
|
||||
} else {
|
||||
try {
|
||||
// try to call the disconnect method on the player
|
||||
MinecraftMethods.getDisconnectMethod(this.playerConnection.getClass()).invoke(this.playerConnection, message);
|
||||
} catch (Exception exception) {
|
||||
throw new IllegalArgumentException("Unable to disconnect the current injector", exception);
|
||||
}
|
||||
}
|
||||
public boolean hasValidPlayer() {
|
||||
return this.player != null && !(player instanceof TemporaryPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return this.channel.isActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -424,139 +387,97 @@ public class NettyChannelInjector implements Injector {
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return this.closed;
|
||||
return this.closed.get();
|
||||
}
|
||||
|
||||
void tryProcessLogin(Object packet) {
|
||||
// check if the given packet is a login packet
|
||||
if (LOGIN_PACKET_START_CLASS != null && LOGIN_PACKET_START_CLASS.equals(packet.getClass())) {
|
||||
PacketType.Protocol getInboundProtocol() {
|
||||
if (this.inboundProtocolReader != null) {
|
||||
return this.inboundProtocolReader.getProtocol();
|
||||
}
|
||||
return getCurrentProtocol(PacketType.Sender.CLIENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any plugin registed listeners or built-in listeners for the
|
||||
* given packet type exist. See {@link #processInbound(ChannelHandlerContext, PacketContainer)}
|
||||
*
|
||||
* @param type the packet type
|
||||
* @return true if the packet type has listeners; otherwise false
|
||||
*/
|
||||
boolean hasInboundListener(PacketType type) {
|
||||
// always return true for types of the built-in listener (see #processInbound)
|
||||
return type == PacketType.Handshake.Client.SET_PROTOCOL ||
|
||||
type == PacketType.Login.Client.START ||
|
||||
// check for plugin registed listener
|
||||
this.listenerManager.hasInboundListener(type);
|
||||
}
|
||||
|
||||
void processInbound(ChannelHandlerContext ctx, PacketContainer packet) {
|
||||
// process set protocol packets for the protocol version
|
||||
if (packet.getType() == PacketType.Handshake.Client.SET_PROTOCOL) {
|
||||
Integer protocolVersion = packet.getIntegers().readSafely(0);
|
||||
if (protocolVersion != null) {
|
||||
this.channel.attr(PROTOCOL_VERSION).set(protocolVersion);
|
||||
}
|
||||
}
|
||||
|
||||
// process login packets for the player name
|
||||
if (packet.getType() == PacketType.Login.Client.START) {
|
||||
String username;
|
||||
if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
|
||||
// 1.19 removed the profile from the packet and now sends the plain username directly
|
||||
// ensure that the game profile accessor is available
|
||||
if (LOGIN_PROFILE_ACCESSOR == null) {
|
||||
LOGIN_PROFILE_ACCESSOR = Accessors.getFieldAccessor(LOGIN_PACKET_START_CLASS, String.class, true);
|
||||
}
|
||||
// 1.19 replaced the gameprofile with username and uuid field
|
||||
username = packet.getStrings().readSafely(0);
|
||||
} else {
|
||||
WrappedGameProfile profile = packet.getGameProfiles().readSafely(0);
|
||||
username = profile != null ? profile.getName() : null;
|
||||
}
|
||||
|
||||
// get the username from the packet
|
||||
String username = (String) LOGIN_PROFILE_ACCESSOR.get(packet);
|
||||
|
||||
// cache the injector and the player name
|
||||
if (username != null) {
|
||||
this.playerName = username;
|
||||
this.injectionFactory.cacheInjector(username, this);
|
||||
} else {
|
||||
// ensure that the game profile accessor is available
|
||||
if (LOGIN_PROFILE_ACCESSOR == null) {
|
||||
LOGIN_PROFILE_ACCESSOR = Accessors.getFieldAccessor(
|
||||
LOGIN_PACKET_START_CLASS,
|
||||
MinecraftReflection.getGameProfileClass(),
|
||||
true);
|
||||
}
|
||||
|
||||
// the client only sends the name but the server wraps it into a GameProfile, so here we are
|
||||
WrappedGameProfile profile = WrappedGameProfile.fromHandle(LOGIN_PROFILE_ACCESSOR.get(packet));
|
||||
|
||||
// cache the injector and the player name
|
||||
this.playerName = profile.getName();
|
||||
this.injectionFactory.cacheInjector(profile.getName(), this);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// protocol version begin
|
||||
if (PACKET_PROTOCOL_CLASS != null && PACKET_PROTOCOL_CLASS.equals(packet.getClass())) {
|
||||
// ensure the protocol version accessor is available
|
||||
if (PROTOCOL_VERSION_ACCESSOR == null) {
|
||||
try {
|
||||
Field ver = FuzzyReflection.fromClass(PACKET_PROTOCOL_CLASS, true).getField(FuzzyFieldContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.typeExact(int.class)
|
||||
.build());
|
||||
PROTOCOL_VERSION_ACCESSOR = Accessors.getFieldAccessor(ver);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
// unable to resolve that field, continue no-op
|
||||
PROTOCOL_VERSION_ACCESSOR = NO_OP_ACCESSOR;
|
||||
}
|
||||
}
|
||||
|
||||
// read the protocol version from the field if available
|
||||
if (PROTOCOL_VERSION_ACCESSOR != NO_OP_ACCESSOR) {
|
||||
int protocolVersion = (int) PROTOCOL_VERSION_ACCESSOR.get(packet);
|
||||
this.wrappedChannel.attr(PROTOCOL_VERSION).set(protocolVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void rewriteChannelField() {
|
||||
// check if we need to rewrite the channel or if the channel is already correct (prevent wrapping a wrapped channel)
|
||||
Object currentChannel = this.channelField.get(this.networkManager);
|
||||
if (currentChannel instanceof NettyChannelProxy) {
|
||||
return;
|
||||
}
|
||||
|
||||
// the field is not correct, rewrite now to our handler
|
||||
Channel ch = new NettyChannelProxy(this.wrappedChannel, new NettyEventLoopProxy(this.wrappedChannel.eventLoop(), this) {
|
||||
@Override
|
||||
protected Runnable doProxyRunnable(Runnable original) {
|
||||
return NettyChannelInjector.this.processOutbound(original);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> Callable<T> doProxyCallable(Callable<T> original) {
|
||||
return NettyChannelInjector.this.processOutbound(original);
|
||||
}
|
||||
}, this);
|
||||
this.channelField.set(this.networkManager, ch);
|
||||
}
|
||||
|
||||
private void ensureInEventLoop(Runnable runnable) {
|
||||
this.ensureInEventLoop(this.wrappedChannel.eventLoop(), runnable);
|
||||
}
|
||||
|
||||
private void ensureInEventLoop(EventLoop eventLoop, Runnable runnable) {
|
||||
if (eventLoop.inEventLoop()) {
|
||||
runnable.run();
|
||||
// invoke plugin listeners
|
||||
if (this.listenerManager.hasInboundListener(packet.getType())) {
|
||||
this.processInboundInternal(ctx, packet);
|
||||
} else {
|
||||
eventLoop.execute(runnable);
|
||||
ctx.fireChannelRead(packet.getHandle());
|
||||
}
|
||||
}
|
||||
|
||||
void processInboundPacket(ChannelHandlerContext ctx, Object packet, PacketType packetType) {
|
||||
if (this.channelListener.hasMainThreadListener(packetType) && !this.server.isPrimaryThread()) {
|
||||
// not on the main thread but we are required to be - re-schedule the packet on the main thread
|
||||
ProtocolLibrary.getScheduler().runTask(
|
||||
() -> this.processInboundPacket(ctx, packet, packetType));
|
||||
private void processInboundInternal(ChannelHandlerContext ctx, PacketContainer packetContainer) {
|
||||
if (this.listenerManager.hasMainThreadListener(packetContainer.getType()) && !Bukkit.isPrimaryThread()) {
|
||||
// not on the main thread but we are required to be reschedule the packet on the
|
||||
// main thread
|
||||
ProtocolLibrary.getScheduler().runTask(() -> this.processInboundInternal(ctx, packetContainer));
|
||||
return;
|
||||
}
|
||||
|
||||
// call packet handlers, a null result indicates that we shouldn't change anything
|
||||
PacketContainer packetContainer = new PacketContainer(packetType, packet);
|
||||
PacketEvent interceptionResult = this.channelListener.onPacketReceiving(this, packetContainer, null);
|
||||
if (interceptionResult == null) {
|
||||
// create event and invoke listeners
|
||||
PacketEvent event = PacketEvent.fromClient(this, packetContainer, this.player);
|
||||
this.listenerManager.invokeInboundPacketListeners(event);
|
||||
|
||||
// get packet of event
|
||||
Object packet = event.getPacket().getHandle();
|
||||
|
||||
// fire the intercepted packet down the pipeline if it wasn't cancelled and isn't null
|
||||
if (!event.isCancelled() && packet != null) {
|
||||
this.ensureInEventLoop(ctx.channel().eventLoop(), () -> ctx.fireChannelRead(packet));
|
||||
return;
|
||||
}
|
||||
|
||||
// fire the intercepted packet down the pipeline if it wasn't cancelled
|
||||
if (!interceptionResult.isCancelled()) {
|
||||
this.ensureInEventLoop(
|
||||
ctx.channel().eventLoop(),
|
||||
() -> ctx.fireChannelRead(interceptionResult.getPacket().getHandle()));
|
||||
|
||||
// check if there were any post events added the packet after we fired it down the pipeline
|
||||
// we use this way as we don't want to construct a new network manager accidentally
|
||||
NetworkMarker marker = NetworkMarker.getNetworkMarker(interceptionResult);
|
||||
NetworkMarker marker = NetworkMarker.getNetworkMarker(event);
|
||||
if (marker != null) {
|
||||
this.networkProcessor.invokePostEvent(interceptionResult, marker);
|
||||
this.networkProcessor.invokePostEvent(event, marker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<T> T processOutbound(T action) {
|
||||
// get the accessor to the packet field
|
||||
// if we are unable to look up the accessor then just return the runnable, probably nothing of our business
|
||||
// try to get packet field accessor or return on failure
|
||||
FieldAccessor packetAccessor = this.lookupPacketAccessor(action);
|
||||
if (packetAccessor == NO_OP_ACCESSOR) {
|
||||
if (packetAccessor == FieldAccessor.NO_OP_ACCESSOR) {
|
||||
return action;
|
||||
}
|
||||
|
||||
@@ -583,7 +504,7 @@ public class NettyChannelInjector implements Injector {
|
||||
|
||||
PacketType.Protocol protocol = this.getCurrentProtocol(PacketType.Sender.SERVER);
|
||||
if (protocol == Protocol.UNKNOWN) {
|
||||
ProtocolLogger.debug("skipping unknown protocol for {0}", packet.getClass());
|
||||
ProtocolLogger.debug("skipping unknown outbound protocol for {0}", packet.getClass());
|
||||
return action;
|
||||
}
|
||||
|
||||
@@ -594,30 +515,28 @@ public class NettyChannelInjector implements Injector {
|
||||
}
|
||||
|
||||
// no listener and no marker - no magic :)
|
||||
if (!this.channelListener.hasOutboundListener(packetType) && marker == null && !MinecraftReflection.isBundlePacket(packet.getClass())) {
|
||||
if (!this.listenerManager.hasOutboundListener(packetType) && marker == null && !MinecraftReflection.isBundlePacket(packet.getClass())) {
|
||||
return action;
|
||||
}
|
||||
|
||||
// ensure that we are on the main thread if we need to
|
||||
if (this.channelListener.hasMainThreadListener(packetType) && !this.server.isPrimaryThread()) {
|
||||
if (this.listenerManager.hasMainThreadListener(packetType) && !Bukkit.isPrimaryThread()) {
|
||||
// not on the main thread but we are required to be - re-schedule the packet on the main thread
|
||||
ProtocolLibrary.getScheduler().runTask(
|
||||
() -> this.sendServerPacket(packet, null, true));
|
||||
ProtocolLibrary.getScheduler().runTask(() -> this.sendClientboundPacket(packet, null, true));
|
||||
return null;
|
||||
}
|
||||
|
||||
// call all listeners which are listening to the outbound packet, if any
|
||||
// null indicates that no listener was affected by the packet, meaning that we can directly send the original packet
|
||||
// create event and invoke listeners
|
||||
PacketContainer packetContainer = new PacketContainer(packetType, packet);
|
||||
PacketEvent event = this.channelListener.onPacketSending(this, packetContainer, marker);
|
||||
if (event == null) {
|
||||
return action;
|
||||
}
|
||||
PacketEvent event = PacketEvent.fromServer(this, packetContainer, marker, this.player);
|
||||
this.listenerManager.invokeOutboundPacketListeners(event);
|
||||
|
||||
// get packet of event
|
||||
Object interceptedPacket = event.getPacket().getHandle();
|
||||
|
||||
// if the event wasn't cancelled by this action we must recheck if the packet changed during the method call
|
||||
if (!event.isCancelled()) {
|
||||
if (!event.isCancelled() && interceptedPacket != null) {
|
||||
// rewrite the packet in the given action if the packet was changed during the event call
|
||||
Object interceptedPacket = event.getPacket().getHandle();
|
||||
if (interceptedPacket != packet) {
|
||||
packetAccessor.set(action, interceptedPacket);
|
||||
}
|
||||
@@ -667,37 +586,29 @@ public class NettyChannelInjector implements Injector {
|
||||
}
|
||||
|
||||
private FieldAccessor lookupPacketAccessor(Object action) {
|
||||
return PACKET_ACCESSORS.computeIfAbsent(action.getClass(), clazz -> {
|
||||
return PACKET_ACCESSORS.computeIfAbsent(action.getClass(), key -> {
|
||||
try {
|
||||
// find the field
|
||||
Field packetField = FuzzyReflection.fromClass(clazz, true).getField(FuzzyFieldContract
|
||||
.newBuilder()
|
||||
Field packetField = FuzzyReflection.fromClass(key, true).getField(
|
||||
FuzzyFieldContract.newBuilder()
|
||||
.typeSuperOf(MinecraftReflection.getPacketClass())
|
||||
.build());
|
||||
return Accessors.getFieldAccessor(packetField);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
// no such field found :(
|
||||
return NO_OP_ACCESSOR;
|
||||
// return noop accessor because computeIfAbsent interprets null as a missing key
|
||||
return FieldAccessor.NO_OP_ACCESSOR;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Object getPlayerConnection() {
|
||||
// resolve the player connection if needed
|
||||
if (this.playerConnection == null) {
|
||||
Player target = this.getPlayer();
|
||||
if (target == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.playerConnection = MinecraftFields.getPlayerConnection(target);
|
||||
}
|
||||
|
||||
// cannot be null at this point
|
||||
return this.playerConnection;
|
||||
private void ensureInEventLoop(Runnable runnable) {
|
||||
this.ensureInEventLoop(this.channel.eventLoop(), runnable);
|
||||
}
|
||||
|
||||
public Channel getWrappedChannel() {
|
||||
return this.wrappedChannel;
|
||||
private void ensureInEventLoop(EventLoop eventLoop, Runnable runnable) {
|
||||
if (eventLoop.inEventLoop()) {
|
||||
runnable.run();
|
||||
} else {
|
||||
eventLoop.execute(runnable);
|
||||
}
|
||||
}
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
package com.comphenix.protocol.injector.netty.channel;
|
||||
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.injector.temporary.MinimalInjector;
|
||||
import java.net.SocketAddress;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
final class NettyChannelMinimalInjector implements MinimalInjector {
|
||||
|
||||
private final NettyChannelInjector injector;
|
||||
|
||||
public NettyChannelMinimalInjector(NettyChannelInjector injector) {
|
||||
this.injector = injector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getAddress() {
|
||||
return this.injector.getWrappedChannel().remoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(String message) {
|
||||
this.injector.disconnect(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) {
|
||||
this.injector.sendServerPacket(packet, marker, filtered);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getPlayer() {
|
||||
return this.injector.getPlayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return this.injector.getWrappedChannel().isActive();
|
||||
}
|
||||
|
||||
public NettyChannelInjector getInjector() {
|
||||
return this.injector;
|
||||
}
|
||||
}
|
||||
+13
-32
@@ -1,5 +1,15 @@
|
||||
package com.comphenix.protocol.injector.netty.channel;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Spliterator;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
@@ -10,21 +20,12 @@ import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.ProgressivePromise;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
import io.netty.util.concurrent.ScheduledFuture;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Spliterator;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* An abstract event loop implementation which delegates all calls to a given event loop, but proxies all calls which
|
||||
* schedule something on the event loop to methods which decide what should happen to the scheduled task.
|
||||
*/
|
||||
abstract class NettyEventLoopProxy implements EventLoop {
|
||||
final class NettyEventLoopProxy implements EventLoop {
|
||||
|
||||
private static final Callable<?> EMPTY_CALLABLE = () -> null;
|
||||
private static final Runnable EMPTY_RUNNABLE = () -> {
|
||||
@@ -40,7 +41,7 @@ abstract class NettyEventLoopProxy implements EventLoop {
|
||||
|
||||
private Runnable proxyRunnable(Runnable original) {
|
||||
// execute the proxy and check if we need to do anything
|
||||
Runnable proxied = this.doProxyRunnable(original);
|
||||
Runnable proxied = this.injector.processOutbound(original);
|
||||
if (proxied != null && proxied == original) {
|
||||
// was not changed, we need to mark the packet as processed manually
|
||||
return () -> {
|
||||
@@ -55,7 +56,7 @@ abstract class NettyEventLoopProxy implements EventLoop {
|
||||
|
||||
private <T> Callable<T> proxyCallable(Callable<T> original) {
|
||||
// execute the proxy and check if we need to do anything
|
||||
Callable<T> proxied = this.doProxyCallable(original);
|
||||
Callable<T> proxied = this.injector.processOutbound(original);
|
||||
if (proxied != null && proxied == original) {
|
||||
// was not changed, we need to mark the packet as processed manually
|
||||
return () -> {
|
||||
@@ -68,26 +69,6 @@ abstract class NettyEventLoopProxy implements EventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxies the given runnable. The returned runnable will be executed instead of the original. If this method returns
|
||||
* null a no-op runnable will be scheduled instead, preventing the original action from happening.
|
||||
*
|
||||
* @param original the runnable to proxy.
|
||||
* @return the runnable to execute instead, null to execute no action.
|
||||
*/
|
||||
protected abstract Runnable doProxyRunnable(Runnable original);
|
||||
|
||||
/**
|
||||
* Proxies the given callable. The returned callable will be executed instead of the original. If this method returns
|
||||
* null a callable which always returns null will be scheduled instead, preventing the original action from
|
||||
* happening.
|
||||
*
|
||||
* @param original the callable to proxy.
|
||||
* @param <T> the return type of the original callable.
|
||||
* @return the callable to execute instead of the original, null to use a no-op callable instead.
|
||||
*/
|
||||
protected abstract <T> Callable<T> doProxyCallable(Callable<T> original);
|
||||
|
||||
@Override
|
||||
public EventLoopGroup parent() {
|
||||
return this.delegate.parent();
|
||||
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
package com.comphenix.protocol.injector.netty.channel;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLogger;
|
||||
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 com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
|
||||
/**
|
||||
* This class facilitates the invocation of methods on the current packet listener.
|
||||
* 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
|
||||
* operation falls back to similar methods available in the network manager.
|
||||
*
|
||||
* <p>It is important to note that this class does not handle exceptions internally.
|
||||
* Instead, it propagates them to the caller. During the initialization phase, the
|
||||
* class will throw an exception if the necessary methods in the network manager are
|
||||
* not available, ensuring that these dependencies are addressed early in the runtime.
|
||||
*/
|
||||
public class PacketListenerInvoker {
|
||||
|
||||
private static final Class<?> PACKET_LISTENER_CLASS = MinecraftReflection.getMinecraftClass("network.PacketListener", "PacketListener");
|
||||
private static final Class<?> GAME_PACKET_LISTENER_CLASS = MinecraftReflection.getPlayerConnectionClass();
|
||||
private static final Class<?> COMMON_PACKET_LISTENER_CLASS = MinecraftReflection.getNullableNMS("server.network.ServerCommonPacketListenerImpl");
|
||||
private static final Class<?> PREFERRED_PACKET_LISTENER_CLASS = COMMON_PACKET_LISTENER_CLASS != null ? COMMON_PACKET_LISTENER_CLASS : GAME_PACKET_LISTENER_CLASS;
|
||||
|
||||
private static final MethodAccessor PACKET_LISTENER_SEND = getPacketListenerSend();
|
||||
private static final MethodAccessor PACKET_LISTENER_DISCONNECT = getPacketListenerDisconnect();
|
||||
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_DISCONNECT = getNetworkManagerDisconnect();
|
||||
private static final MethodAccessor NETWORK_MANAGER_PACKET_LISTENER = getNetworkManagerPacketListener();
|
||||
|
||||
public static void ensureStaticInitializedWithoutError() {
|
||||
}
|
||||
|
||||
private static MethodAccessor getPacketListenerSend() {
|
||||
FuzzyReflection packetListener = FuzzyReflection.fromClass(PREFERRED_PACKET_LISTENER_CLASS);
|
||||
|
||||
List<Method> send = packetListener.getMethodList(FuzzyMethodContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.returnTypeVoid()
|
||||
.parameterCount(1)
|
||||
.parameterExactType(MinecraftReflection.getPacketClass(), 0)
|
||||
.build());
|
||||
|
||||
if (send.isEmpty()) {
|
||||
ProtocolLogger.debug("Can't get packet listener send method");
|
||||
return null;
|
||||
}
|
||||
|
||||
return Accessors.getMethodAccessor(send.get(0));
|
||||
}
|
||||
|
||||
private static MethodAccessor getPacketListenerDisconnect() {
|
||||
FuzzyReflection packetListener = FuzzyReflection.fromClass(PREFERRED_PACKET_LISTENER_CLASS);
|
||||
|
||||
List<Method> disconnect = packetListener.getMethodList(FuzzyMethodContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.returnTypeVoid()
|
||||
.parameterCount(1)
|
||||
.parameterExactType(MinecraftReflection.getIChatBaseComponentClass(), 0)
|
||||
.build());
|
||||
|
||||
if (disconnect.isEmpty()) {
|
||||
disconnect = packetListener.getMethodList(FuzzyMethodContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.returnTypeVoid()
|
||||
.nameRegex("disconnect.*")
|
||||
.parameterCount(1)
|
||||
.parameterExactType(String.class, 0)
|
||||
.build());
|
||||
}
|
||||
|
||||
if (disconnect.isEmpty()) {
|
||||
ProtocolLogger.debug("Can't get packet listener disconnect method");
|
||||
return null;
|
||||
}
|
||||
|
||||
return Accessors.getMethodAccessor(disconnect.get(0));
|
||||
}
|
||||
|
||||
private static boolean doesPacketListenerDisconnectUseComponent() {
|
||||
if (PACKET_LISTENER_DISCONNECT != null) {
|
||||
Parameter reason = PACKET_LISTENER_DISCONNECT.getMethod().getParameters()[0];
|
||||
return MinecraftReflection.isIChatBaseComponent(reason.getClass());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static MethodAccessor getNetworkManagerSend() {
|
||||
FuzzyReflection networkManager = FuzzyReflection.fromClass(MinecraftReflection.getNetworkManagerClass());
|
||||
|
||||
Method send = networkManager.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.returnTypeVoid()
|
||||
.parameterCount(1)
|
||||
.parameterExactType(MinecraftReflection.getPacketClass(), 0)
|
||||
.build());
|
||||
|
||||
return Accessors.getMethodAccessor(send);
|
||||
}
|
||||
|
||||
private static MethodAccessor getNetworkManagerDisconnect() {
|
||||
FuzzyReflection networkManager = FuzzyReflection.fromClass(MinecraftReflection.getNetworkManagerClass());
|
||||
|
||||
Method disconnect = networkManager.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.returnTypeVoid()
|
||||
.parameterCount(1)
|
||||
.parameterExactType(MinecraftReflection.getIChatBaseComponentClass(), 0)
|
||||
.build());
|
||||
|
||||
return Accessors.getMethodAccessor(disconnect);
|
||||
}
|
||||
|
||||
private static MethodAccessor getNetworkManagerPacketListener() {
|
||||
FuzzyReflection networkManager = FuzzyReflection.fromClass(MinecraftReflection.getNetworkManagerClass());
|
||||
|
||||
Method packetListener = networkManager.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.returnTypeExact(PACKET_LISTENER_CLASS)
|
||||
.parameterCount(0)
|
||||
.build());
|
||||
|
||||
return Accessors.getMethodAccessor(packetListener);
|
||||
}
|
||||
|
||||
private final Object networkManager;
|
||||
private final AtomicReference<Object> packetListener = new AtomicReference<>(null);
|
||||
|
||||
PacketListenerInvoker(Object networkManager) {
|
||||
if (!MinecraftReflection.is(MinecraftReflection.getNetworkManagerClass(), networkManager)) {
|
||||
throw new IllegalArgumentException("Given NetworkManager isn't an isntance of NetworkManager");
|
||||
}
|
||||
this.networkManager = networkManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current packet listener associated with this network manager.
|
||||
*
|
||||
* <p>This method ensures thread-safety and returns the packet listener only if it is an
|
||||
* instance of the preferred class. If the packet listener has changed or does not match
|
||||
* the preferred class, it returns {@code null}.
|
||||
*
|
||||
* @return the current packet listener if it meets the required criteria, otherwise {@code null}.
|
||||
*/
|
||||
private Object getPacketListener() {
|
||||
// Retrieve the current packet listener from the network manager using reflection.
|
||||
Object packetListener = NETWORK_MANAGER_PACKET_LISTENER.invoke(this.networkManager);
|
||||
|
||||
// Perform a thread-safe check to see if the packet listener has changed since the last retrieval.
|
||||
if (!this.packetListener.compareAndSet(packetListener, packetListener)) {
|
||||
// If the packet listener has changed, attempt to update the cached listener to the new instance,
|
||||
// or invalidate the cached object if it does not match the preferred type.
|
||||
if (PREFERRED_PACKET_LISTENER_CLASS.isInstance(packetListener)) {
|
||||
this.packetListener.set(packetListener);
|
||||
} else {
|
||||
this.packetListener.set(null);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the currently cached packet listener, which may be null if it does not match the preferred type.
|
||||
return this.packetListener.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public void send(Object packet) {
|
||||
Object packetListener = this.getPacketListener();
|
||||
if (PACKET_LISTENER_SEND != null && packetListener != null) {
|
||||
PACKET_LISTENER_SEND.invoke(packetListener, packet);
|
||||
} else {
|
||||
NETWORK_MANAGER_SEND.invoke(this.networkManager, packet);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects the player using the current packet listener if available and valid; otherwise,
|
||||
* falls back to the network manager.
|
||||
*
|
||||
* @param reason The reason for the disconnection.
|
||||
*/
|
||||
public void disconnect(String reason) {
|
||||
Object packetListener = this.getPacketListener();
|
||||
boolean hasPacketListener = PACKET_LISTENER_DISCONNECT != null && packetListener != null;
|
||||
|
||||
Object wrapped = reason;
|
||||
if (!hasPacketListener || DOES_PACKET_LISTENER_DISCONNECT_USE_COMPONENT) {
|
||||
wrapped = WrappedChatComponent.fromText(reason).getHandle();
|
||||
}
|
||||
|
||||
if (hasPacketListener) {
|
||||
PACKET_LISTENER_DISCONNECT.invoke(packetListener, wrapped);
|
||||
} else {
|
||||
NETWORK_MANAGER_DISCONNECT.invoke(this.networkManager, wrapped);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-14
@@ -1,9 +1,10 @@
|
||||
package com.comphenix.protocol.injector.netty.manager;
|
||||
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.error.Report;
|
||||
import com.comphenix.protocol.error.ReportType;
|
||||
import com.comphenix.protocol.injector.netty.channel.InjectionFactory;
|
||||
import com.comphenix.protocol.injector.temporary.TemporaryPlayerFactory;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
|
||||
@@ -11,18 +12,15 @@ final class InjectionChannelInboundHandler extends ChannelInboundHandlerAdapter
|
||||
|
||||
private static final ReportType CANNOT_INJECT_CHANNEL = new ReportType("Unable to inject incoming channel %s.");
|
||||
|
||||
private final InjectionFactory factory;
|
||||
private final NetworkManagerInjector listener;
|
||||
private final TemporaryPlayerFactory playerFactory;
|
||||
private final ErrorReporter errorReporter;
|
||||
private final InjectionFactory injectionFactory;
|
||||
|
||||
public InjectionChannelInboundHandler(
|
||||
InjectionFactory factory,
|
||||
NetworkManagerInjector listener,
|
||||
TemporaryPlayerFactory playerFactory
|
||||
ErrorReporter errorReporter,
|
||||
InjectionFactory injectionFactory
|
||||
) {
|
||||
this.factory = factory;
|
||||
this.listener = listener;
|
||||
this.playerFactory = playerFactory;
|
||||
this.errorReporter = errorReporter;
|
||||
this.injectionFactory = injectionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -35,11 +33,11 @@ final class InjectionChannelInboundHandler extends ChannelInboundHandlerAdapter
|
||||
// We're first checking if the factory is still open, just might be a delay between accepting the connection
|
||||
// (which adds this handler to the pipeline) and the actual channelActive call. If the injector is closed at
|
||||
// that point we might accidentally trigger class loads which result in exceptions.
|
||||
if (!this.factory.isClosed()) {
|
||||
if (!this.injectionFactory.isClosed()) {
|
||||
try {
|
||||
this.factory.fromChannel(ctx.channel(), this.listener, this.playerFactory).inject();
|
||||
this.injectionFactory.fromChannel(ctx.channel()).inject();
|
||||
} catch (Exception exception) {
|
||||
this.listener.getReporter().reportDetailed(this, Report.newBuilder(CANNOT_INJECT_CHANNEL)
|
||||
this.errorReporter.reportDetailed(this, Report.newBuilder(CANNOT_INJECT_CHANNEL)
|
||||
.messageParam(ctx.channel())
|
||||
.error(exception)
|
||||
.build());
|
||||
@@ -52,7 +50,7 @@ final class InjectionChannelInboundHandler extends ChannelInboundHandlerAdapter
|
||||
|
||||
@Override
|
||||
public boolean isSharable() {
|
||||
// we do it this way to prevent the lookup overheat
|
||||
// we do it this way to prevent the lookup overhead
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -28,7 +28,7 @@ final class InjectionChannelInitializer extends ChannelInboundHandlerAdapter {
|
||||
|
||||
@Override
|
||||
public boolean isSharable() {
|
||||
// we do it this way to prevent the lookup overheat
|
||||
// we do it this way to prevent the lookup overhead
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+14
-90
@@ -7,21 +7,11 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLogger;
|
||||
import com.comphenix.protocol.error.ErrorReporter;
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.ListenerInvoker;
|
||||
import com.comphenix.protocol.injector.netty.ChannelListener;
|
||||
import com.comphenix.protocol.injector.ListenerManager;
|
||||
import com.comphenix.protocol.injector.netty.Injector;
|
||||
import com.comphenix.protocol.injector.netty.channel.InjectionFactory;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||
import com.comphenix.protocol.injector.temporary.TemporaryPlayerFactory;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
@@ -30,107 +20,45 @@ 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 implements ChannelListener {
|
||||
public class NetworkManagerInjector {
|
||||
|
||||
private static final String INBOUND_INJECT_HANDLER_NAME = "protocol_lib_inbound_inject";
|
||||
private static final TemporaryPlayerFactory PLAYER_FACTORY = new TemporaryPlayerFactory();
|
||||
|
||||
// all list fields which we've overridden and need to revert to a non-proxying list afterwards
|
||||
private final Set<Pair<Object, FieldAccessor>> overriddenLists = new HashSet<>();
|
||||
|
||||
private final ErrorReporter errorReporter;
|
||||
private final ListenerInvoker listenerInvoker;
|
||||
private final InjectionFactory injectionFactory;
|
||||
|
||||
// injectors based on this "global" injector
|
||||
private final PlayerInjectionHandler playerInjectionHandler;
|
||||
|
||||
// netty handler
|
||||
private final InjectionChannelInitializer pipelineInjectorHandler;
|
||||
|
||||
private boolean debug = false;
|
||||
|
||||
// status of this injector
|
||||
private boolean closed = false;
|
||||
private boolean injected = false;
|
||||
|
||||
public NetworkManagerInjector(Plugin plugin, Server server, ListenerInvoker listenerInvoker, ErrorReporter reporter) {
|
||||
public NetworkManagerInjector(Plugin plugin, ListenerManager listenerManager, ErrorReporter reporter) {
|
||||
this.errorReporter = reporter;
|
||||
this.listenerInvoker = listenerInvoker;
|
||||
this.injectionFactory = new InjectionFactory(plugin, server, reporter);
|
||||
this.injectionFactory = new InjectionFactory(plugin, reporter, listenerManager);
|
||||
|
||||
// hooking netty handlers
|
||||
InjectionChannelInboundHandler injectionHandler = new InjectionChannelInboundHandler(
|
||||
this.injectionFactory,
|
||||
this,
|
||||
PLAYER_FACTORY);
|
||||
this.pipelineInjectorHandler = new InjectionChannelInitializer(INBOUND_INJECT_HANDLER_NAME, injectionHandler);
|
||||
|
||||
// other injectors
|
||||
this.playerInjectionHandler = new NetworkManagerPlayerInjector(
|
||||
this,
|
||||
this.errorReporter,
|
||||
this.injectionFactory);
|
||||
this.pipelineInjectorHandler = new InjectionChannelInitializer(INBOUND_INJECT_HANDLER_NAME, injectionHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketEvent onPacketSending(Injector injector, PacketContainer packet, NetworkMarker marker) {
|
||||
// check if we need to intercept the packet
|
||||
Class<?> packetClass = packet.getHandle().getClass();
|
||||
if (marker != null || MinecraftReflection.isBundlePacket(packetClass) || hasOutboundListener(packet.getType())) {
|
||||
// wrap packet and construct the event
|
||||
PacketEvent packetEvent = PacketEvent.fromServer(this, packet, marker, injector.getPlayer());
|
||||
|
||||
// post to all listeners, then return the packet event we constructed
|
||||
this.listenerInvoker.invokePacketSending(packetEvent);
|
||||
return packetEvent;
|
||||
}
|
||||
|
||||
// no listener so there is no change we need to apply
|
||||
return null;
|
||||
public Injector getInjector(Player player) {
|
||||
return this.injectionFactory.fromPlayer(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketEvent onPacketReceiving(Injector injector, PacketContainer packet, NetworkMarker marker) {
|
||||
if (marker != null || hasInboundListener(packet.getType())) {
|
||||
PacketEvent packetEvent = PacketEvent.fromClient(this, packet, marker, injector.getPlayer());
|
||||
|
||||
// post to all listeners, then return the packet event we constructed
|
||||
this.listenerInvoker.invokePacketReceiving(packetEvent);
|
||||
return packetEvent;
|
||||
}
|
||||
|
||||
// no listener so there is no change we need to apply
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasInboundListener(PacketType packetType) {
|
||||
return this.listenerInvoker.hasInboundListener(packetType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasOutboundListener(PacketType packetType) {
|
||||
return this.listenerInvoker.hasOutboundListener(packetType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainThreadListener(PacketType packetType) {
|
||||
return this.listenerInvoker.hasMainThreadListener(packetType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ErrorReporter getReporter() {
|
||||
return this.errorReporter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebug() {
|
||||
return this.debug;
|
||||
}
|
||||
|
||||
public void setDebug(boolean debug) {
|
||||
this.debug = debug;
|
||||
public Injector getInjector(Channel channel) {
|
||||
return injectionFactory.fromChannel(channel);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -231,8 +159,4 @@ public class NetworkManagerInjector implements ChannelListener {
|
||||
this.overriddenLists.clear();
|
||||
this.injectionFactory.close();
|
||||
}
|
||||
|
||||
public PlayerInjectionHandler getPlayerInjectionHandler() {
|
||||
return this.playerInjectionHandler;
|
||||
}
|
||||
}
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
package com.comphenix.protocol.injector.netty.manager;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.injector.netty.ChannelListener;
|
||||
import com.comphenix.protocol.injector.netty.Injector;
|
||||
import com.comphenix.protocol.injector.netty.channel.InjectionFactory;
|
||||
import com.comphenix.protocol.injector.netty.channel.NettyChannelInjector;
|
||||
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
final class NetworkManagerPlayerInjector implements PlayerInjectionHandler {
|
||||
|
||||
private final ChannelListener listener;
|
||||
private final InjectionFactory injectionFactory;
|
||||
|
||||
public NetworkManagerPlayerInjector(
|
||||
ChannelListener listener,
|
||||
InjectionFactory injectionFactory
|
||||
) {
|
||||
this.listener = listener;
|
||||
this.injectionFactory = injectionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProtocolVersion(Player player) {
|
||||
return this.injectionFactory.fromPlayer(player, this.listener).getProtocolVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectPlayer(Player player, ConflictStrategy strategy) {
|
||||
this.injectionFactory.fromPlayer(player, this.listener).inject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean uninjectPlayer(Player player) {
|
||||
this.injectionFactory.fromPlayer(player, this.listener).uninject();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters) {
|
||||
this.injectionFactory.fromPlayer(receiver, this.listener).sendServerPacket(packet.getHandle(), marker, filters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveClientPacket(Player player, Object mcPacket) {
|
||||
this.injectionFactory.fromPlayer(player, this.listener).receiveClientPacket(mcPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePlayer(Player player) {
|
||||
this.injectionFactory.fromPlayer(player, this.listener).inject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Channel getChannel(Player player) {
|
||||
Injector injector = this.injectionFactory.fromPlayer(player, this.listener);
|
||||
if (injector instanceof NettyChannelInjector) {
|
||||
return ((NettyChannelInjector) injector).getWrappedChannel();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+102
@@ -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));
|
||||
}
|
||||
}
|
||||
+150
@@ -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) {
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package com.comphenix.protocol.injector.player;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
public interface PlayerInjectionHandler {
|
||||
|
||||
/**
|
||||
* Retrieve the protocol version of the given player.
|
||||
*
|
||||
* @param player - the player.
|
||||
* @return The protocol version, or {@link Integer#MIN_VALUE}.
|
||||
*/
|
||||
int getProtocolVersion(Player player);
|
||||
|
||||
/**
|
||||
* Initialize a player hook, allowing us to read server packets.
|
||||
* <p>
|
||||
* This call will be ignored if there's no listener that can receive the given events.
|
||||
*
|
||||
* @param player - player to hook.
|
||||
* @param strategy - how to handle injection conflicts.
|
||||
*/
|
||||
void injectPlayer(Player player, ConflictStrategy strategy);
|
||||
|
||||
/**
|
||||
* Uninject the given player.
|
||||
*
|
||||
* @param player - player to uninject.
|
||||
* @return TRUE if a player has been uninjected, FALSE otherwise.
|
||||
*/
|
||||
boolean uninjectPlayer(Player player);
|
||||
|
||||
/**
|
||||
* Send the given packet to the given receiver.
|
||||
*
|
||||
* @param receiver - the player receiver.
|
||||
* @param packet - the packet to send.
|
||||
* @param marker - network marker.
|
||||
* @param filters - whether or not to invoke the packet filters.
|
||||
*/
|
||||
void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters);
|
||||
|
||||
/**
|
||||
* Process a packet as if it were sent by the given player.
|
||||
*
|
||||
* @param player - the sender.
|
||||
* @param mcPacket - the packet to process.
|
||||
*/
|
||||
void receiveClientPacket(Player player, Object mcPacket);
|
||||
|
||||
/**
|
||||
* Ensure that packet readers are informed of this player reference.
|
||||
*
|
||||
* @param player - the player to update.
|
||||
*/
|
||||
void updatePlayer(Player player);
|
||||
|
||||
Channel getChannel(Player player);
|
||||
|
||||
/**
|
||||
* How to handle a previously existing player injection.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
enum ConflictStrategy {
|
||||
/**
|
||||
* Override it.
|
||||
*/
|
||||
OVERRIDE,
|
||||
|
||||
/**
|
||||
* Immediately exit.
|
||||
*/
|
||||
BAIL_OUT;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package com.comphenix.protocol.injector.temporary;
|
||||
|
||||
import com.comphenix.protocol.events.NetworkMarker;
|
||||
import java.net.SocketAddress;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Represents an injector that only gives access to a player's socket.
|
||||
*
|
||||
* @author Kristian
|
||||
*/
|
||||
public interface MinimalInjector {
|
||||
|
||||
/**
|
||||
* Retrieve the associated address of this player.
|
||||
*
|
||||
* @return The associated address.
|
||||
*/
|
||||
SocketAddress getAddress();
|
||||
|
||||
/**
|
||||
* Attempt to disconnect the current client.
|
||||
*
|
||||
* @param message - the message to display.
|
||||
*/
|
||||
void disconnect(String message);
|
||||
|
||||
/**
|
||||
* Send a packet to the client.
|
||||
*
|
||||
* @param packet - server packet to send.
|
||||
* @param marker - the network marker.
|
||||
* @param filtered - whether or not the packet will be filtered by our listeners.
|
||||
*/
|
||||
void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered);
|
||||
|
||||
/**
|
||||
* Retrieve the hooked player.
|
||||
*
|
||||
* @return The hooked player.
|
||||
*/
|
||||
Player getPlayer();
|
||||
|
||||
/**
|
||||
* Determines if the player is currently connected.
|
||||
*
|
||||
* @return true if the player is connected.
|
||||
*/
|
||||
boolean isConnected();
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
package com.comphenix.protocol.injector.temporary;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.comphenix.protocol.injector.netty.Injector;
|
||||
|
||||
/**
|
||||
* A temporary player created by ProtocolLib when a true player instance does not exist.
|
||||
* <p>
|
||||
@@ -8,15 +12,17 @@ package com.comphenix.protocol.injector.temporary;
|
||||
*/
|
||||
public class TemporaryPlayer {
|
||||
|
||||
private volatile MinimalInjector injector;
|
||||
protected volatile Injector injector;
|
||||
|
||||
MinimalInjector getInjector() {
|
||||
public Injector getInjector() {
|
||||
return this.injector;
|
||||
}
|
||||
|
||||
void setInjector(MinimalInjector injector) {
|
||||
if (injector == null) {
|
||||
throw new IllegalArgumentException("Injector cannot be NULL.");
|
||||
void setInjector(Injector injector) {
|
||||
Objects.requireNonNull(injector, "injector can't be null");
|
||||
|
||||
if (this.injector != null) {
|
||||
throw new IllegalStateException("Can't redefine injector for temporary player");
|
||||
}
|
||||
|
||||
this.injector = injector;
|
||||
|
||||
+21
-39
@@ -17,17 +17,20 @@
|
||||
|
||||
package com.comphenix.protocol.injector.temporary;
|
||||
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.utility.ByteBuddyFactory;
|
||||
import com.comphenix.protocol.utility.ChatExtensions;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.injector.netty.Injector;
|
||||
import com.comphenix.protocol.utility.ByteBuddyFactory;
|
||||
import com.comphenix.protocol.utility.ChatExtensions;
|
||||
|
||||
import net.bytebuddy.description.ByteCodeElement;
|
||||
import net.bytebuddy.description.modifier.Visibility;
|
||||
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
|
||||
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
|
||||
import net.bytebuddy.implementation.FieldAccessor;
|
||||
import net.bytebuddy.implementation.MethodCall;
|
||||
import net.bytebuddy.implementation.MethodDelegation;
|
||||
import net.bytebuddy.implementation.bind.annotation.AllArguments;
|
||||
import net.bytebuddy.implementation.bind.annotation.FieldValue;
|
||||
@@ -36,8 +39,6 @@ import net.bytebuddy.implementation.bind.annotation.RuntimeType;
|
||||
import net.bytebuddy.implementation.bind.annotation.This;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
import net.bytebuddy.matcher.ElementMatchers;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Create fake player instances that represents pre-authenticated clients.
|
||||
@@ -46,13 +47,15 @@ public class TemporaryPlayerFactory {
|
||||
|
||||
private static final Constructor<? extends Player> PLAYER_CONSTRUCTOR = setupProxyPlayerConstructor();
|
||||
|
||||
private TemporaryPlayerFactory() {}
|
||||
|
||||
/**
|
||||
* Retrieve the injector from a given player if it contains one.
|
||||
*
|
||||
* @param player - the player that may contain a reference to a player injector.
|
||||
* @return The referenced player injector, or NULL if none can be found.
|
||||
*/
|
||||
public static MinimalInjector getInjectorFromPlayer(Player player) {
|
||||
public static Injector getInjectorFromPlayer(Player player) {
|
||||
if (player instanceof TemporaryPlayer) {
|
||||
return ((TemporaryPlayer) player).getInjector();
|
||||
}
|
||||
@@ -65,7 +68,7 @@ public class TemporaryPlayerFactory {
|
||||
* @param player - the player to update.
|
||||
* @param injector - the injector to store.
|
||||
*/
|
||||
public static void setInjectorInPlayer(Player player, MinimalInjector injector) {
|
||||
public static void setInjectorForPlayer(Player player, Injector injector) {
|
||||
((TemporaryPlayer) player).setInjector(injector);
|
||||
}
|
||||
|
||||
@@ -76,11 +79,10 @@ public class TemporaryPlayerFactory {
|
||||
public Object delegate(
|
||||
@This Object obj,
|
||||
@Origin Method method,
|
||||
@FieldValue("server") Server server,
|
||||
@FieldValue("injector") Injector injector,
|
||||
@AllArguments Object... args
|
||||
) throws Throwable {
|
||||
String methodName = method.getName();
|
||||
MinimalInjector injector = ((TemporaryPlayer) obj).getInjector();
|
||||
|
||||
if (injector == null) {
|
||||
throw new IllegalStateException("Unable to find injector.");
|
||||
@@ -92,7 +94,7 @@ public class TemporaryPlayerFactory {
|
||||
} else if (methodName.equals("getAddress")) {
|
||||
return injector.getAddress();
|
||||
} else if (methodName.equals("getServer")) {
|
||||
return server;
|
||||
return Bukkit.getServer();
|
||||
}
|
||||
|
||||
// Handle send message methods
|
||||
@@ -144,21 +146,15 @@ public class TemporaryPlayerFactory {
|
||||
|
||||
try {
|
||||
final Constructor<?> constructor = ByteBuddyFactory.getInstance()
|
||||
.createSubclass(TemporaryPlayer.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
|
||||
.createSubclass(TemporaryPlayer.class, ConstructorStrategy.Default.DEFAULT_CONSTRUCTOR)
|
||||
.name(TemporaryPlayerFactory.class.getPackage().getName() + ".TemporaryPlayerInvocationHandler")
|
||||
.implement(Player.class)
|
||||
|
||||
.defineField("server", Server.class, Visibility.PRIVATE)
|
||||
.defineConstructor(Visibility.PUBLIC)
|
||||
.withParameters(Server.class)
|
||||
.intercept(MethodCall.invoke(TemporaryPlayer.class.getDeclaredConstructor())
|
||||
.andThen(FieldAccessor.ofField("server").setsArgumentAt(0)))
|
||||
.method(callbackFilter)
|
||||
.intercept(implementation)
|
||||
.make()
|
||||
.load(ByteBuddyFactory.getInstance().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
|
||||
.getLoaded()
|
||||
.getDeclaredConstructor(Server.class);
|
||||
.getDeclaredConstructor();
|
||||
return (Constructor<? extends Player>) constructor;
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException("Failed to find Temporary Player constructor!", e);
|
||||
@@ -172,9 +168,9 @@ public class TemporaryPlayerFactory {
|
||||
* @param message - a message.
|
||||
* @return Always NULL.
|
||||
*/
|
||||
private static Object sendMessage(MinimalInjector injector, String message) {
|
||||
private static Object sendMessage(Injector injector, String message) {
|
||||
for (PacketContainer packet : ChatExtensions.createChatPackets(message)) {
|
||||
injector.sendServerPacket(packet.getHandle(), null, false);
|
||||
injector.sendClientboundPacket(packet.getHandle(), null, false);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -197,27 +193,13 @@ public class TemporaryPlayerFactory {
|
||||
* Note that a temporary player has not yet been assigned a name, and thus cannot be
|
||||
* uniquely identified. Use the address instead.
|
||||
*
|
||||
* @param server - the current server.
|
||||
* @return A temporary player instance.
|
||||
*/
|
||||
public Player createTemporaryPlayer(final Server server) {
|
||||
public static Player createTemporaryPlayer() {
|
||||
try {
|
||||
return PLAYER_CONSTRUCTOR.newInstance(server);
|
||||
return PLAYER_CONSTRUCTOR.newInstance();
|
||||
} catch (ReflectiveOperationException exception) {
|
||||
throw new IllegalStateException("Unable to create temporary player", exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a temporary player with the given associated socket injector.
|
||||
*
|
||||
* @param server - the parent server.
|
||||
* @param injector - the referenced socket injector.
|
||||
* @return The temporary player.
|
||||
*/
|
||||
public Player createTemporaryPlayer(Server server, MinimalInjector injector) {
|
||||
Player temporary = this.createTemporaryPlayer(server);
|
||||
((TemporaryPlayer) temporary).setInjector(injector);
|
||||
return temporary;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,21 @@ import java.lang.reflect.Constructor;
|
||||
|
||||
public interface ConstructorAccessor {
|
||||
|
||||
/**
|
||||
* NoOp Accessor, does what is says: nothing.
|
||||
*/
|
||||
static final ConstructorAccessor NO_OP_ACCESSOR = new ConstructorAccessor() {
|
||||
@Override
|
||||
public Object invoke(Object... args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Constructor<?> getConstructor() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Invoke the underlying constructor.
|
||||
*
|
||||
|
||||
@@ -9,6 +9,25 @@ import java.lang.reflect.Field;
|
||||
*/
|
||||
public interface FieldAccessor {
|
||||
|
||||
/**
|
||||
* NoOp Accessor, does what is says: nothing.
|
||||
*/
|
||||
static final FieldAccessor NO_OP_ACCESSOR = new FieldAccessor() {
|
||||
@Override
|
||||
public Object get(Object instance) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Object instance, Object value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Field getField() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the value of a field for a particular instance.
|
||||
*
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
package com.comphenix.protocol.reflect.instances;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
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.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class MinecraftGenerator {
|
||||
|
||||
@@ -58,14 +60,20 @@ public class MinecraftGenerator {
|
||||
try {
|
||||
String name = type.getName();
|
||||
if (name.contains("it.unimi.dsi.fastutil")) {
|
||||
Class<?> clz = Class.forName(name.replace("Map", "OpenHashMap"));
|
||||
return Accessors.getConstructorAccessorOrNull(clz);
|
||||
// convert interface maps to OpenHashMaps
|
||||
if (!name.endsWith("OpenHashMap")) {
|
||||
name = name.replace("Map", "OpenHashMap");
|
||||
}
|
||||
|
||||
Class<?> hashMapClass = Class.forName(name);
|
||||
return Accessors.getConstructorAccessorOrNull(hashMapClass);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return null;
|
||||
return ConstructorAccessor.NO_OP_ACCESSOR;
|
||||
});
|
||||
if (ctor != null) {
|
||||
|
||||
if (ctor != ConstructorAccessor.NO_OP_ACCESSOR) {
|
||||
try {
|
||||
return ctor.invoke();
|
||||
} catch (Exception ignored) {
|
||||
|
||||
@@ -35,12 +35,13 @@ import net.bytebuddy.matcher.ElementMatchers;
|
||||
public final class MinecraftMethods {
|
||||
|
||||
// For player connection
|
||||
private volatile static MethodAccessor sendPacketMethod;
|
||||
private volatile static MethodAccessor disconnectMethod;
|
||||
private volatile static MethodAccessor playerConnectionSendMethod;
|
||||
private volatile static MethodAccessor playerConnectionDisconnectMethod;
|
||||
|
||||
// For network manager
|
||||
private volatile static MethodAccessor networkManagerHandle;
|
||||
private volatile static MethodAccessor networkManagerSend;
|
||||
private volatile static MethodAccessor networkManagerPacketRead;
|
||||
private volatile static MethodAccessor networkManagerDisconnect;
|
||||
|
||||
// For packet
|
||||
private volatile static MethodAccessor packetReadByteBuf;
|
||||
@@ -78,18 +79,18 @@ public final class MinecraftMethods {
|
||||
*
|
||||
* @return The send packet method.
|
||||
*/
|
||||
public static MethodAccessor getSendPacketMethod() {
|
||||
if (sendPacketMethod == null) {
|
||||
public static MethodAccessor getPlayerConnectionSendMethod() {
|
||||
if (playerConnectionSendMethod == null) {
|
||||
FuzzyReflection serverHandlerClass = FuzzyReflection.fromClass(MinecraftReflection.getPlayerConnectionClass());
|
||||
|
||||
try {
|
||||
sendPacketMethod = Accessors.getMethodAccessor(serverHandlerClass.getMethod(FuzzyMethodContract.newBuilder()
|
||||
playerConnectionSendMethod = Accessors.getMethodAccessor(serverHandlerClass.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.parameterCount(1)
|
||||
.returnTypeVoid()
|
||||
.parameterExactType(MinecraftReflection.getPacketClass(), 0)
|
||||
.build()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
sendPacketMethod = Accessors.getMethodAccessor(serverHandlerClass.getMethod(FuzzyMethodContract.newBuilder()
|
||||
playerConnectionSendMethod = Accessors.getMethodAccessor(serverHandlerClass.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.nameRegex("sendPacket.*")
|
||||
.returnTypeVoid()
|
||||
.parameterCount(1)
|
||||
@@ -97,33 +98,41 @@ public final class MinecraftMethods {
|
||||
}
|
||||
}
|
||||
|
||||
return sendPacketMethod;
|
||||
return playerConnectionSendMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the disconnect method for a given player connection.
|
||||
*
|
||||
* @param playerConnection - the player connection.
|
||||
* @return The
|
||||
*/
|
||||
public static MethodAccessor getDisconnectMethod(Class<?> playerConnection) {
|
||||
if (disconnectMethod == null) {
|
||||
FuzzyReflection playerConnectionClass = FuzzyReflection.fromClass(playerConnection);
|
||||
try {
|
||||
disconnectMethod = Accessors.getMethodAccessor(playerConnectionClass.getMethod(FuzzyMethodContract.newBuilder()
|
||||
public static MethodAccessor getPlayerConnectionDisconnectMethod() {
|
||||
if (playerConnectionDisconnectMethod == null) {
|
||||
FuzzyReflection playerConnectionClass = FuzzyReflection.fromClass(MinecraftReflection.getPlayerConnectionClass());
|
||||
|
||||
if (MinecraftVersion.v1_21_0.atOrAbove()) {
|
||||
playerConnectionDisconnectMethod = Accessors.getMethodAccessor(playerConnectionClass.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.returnTypeVoid()
|
||||
.nameRegex("disconnect.*")
|
||||
.parameterCount(1)
|
||||
.parameterExactType(String.class, 0)
|
||||
.parameterExactType(MinecraftReflection.getIChatBaseComponentClass(), 0)
|
||||
.build()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Just assume it's the first String method
|
||||
Method disconnect = playerConnectionClass.getMethodByParameters("disconnect", String.class);
|
||||
disconnectMethod = Accessors.getMethodAccessor(disconnect);
|
||||
} else {
|
||||
try {
|
||||
playerConnectionDisconnectMethod = Accessors.getMethodAccessor(playerConnectionClass.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.returnTypeVoid()
|
||||
.nameRegex("disconnect.*")
|
||||
.parameterCount(1)
|
||||
.parameterExactType(String.class, 0)
|
||||
.build()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Just assume it's the first String method
|
||||
Method disconnect = playerConnectionClass.getMethodByParameters("disconnect", String.class);
|
||||
playerConnectionDisconnectMethod = Accessors.getMethodAccessor(disconnect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return disconnectMethod;
|
||||
return playerConnectionDisconnectMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,8 +140,8 @@ public final class MinecraftMethods {
|
||||
*
|
||||
* @return The handle method.
|
||||
*/
|
||||
public static MethodAccessor getNetworkManagerHandleMethod() {
|
||||
if (networkManagerHandle == null) {
|
||||
public static MethodAccessor getNetworkManagerSendMethod() {
|
||||
if (networkManagerSend == null) {
|
||||
Method handleMethod = FuzzyReflection
|
||||
.fromClass(MinecraftReflection.getNetworkManagerClass(), true)
|
||||
.getMethod(FuzzyMethodContract.newBuilder()
|
||||
@@ -141,10 +150,10 @@ public final class MinecraftMethods {
|
||||
.parameterCount(1)
|
||||
.parameterExactType(MinecraftReflection.getPacketClass(), 0)
|
||||
.build());
|
||||
networkManagerHandle = Accessors.getMethodAccessor(handleMethod);
|
||||
networkManagerSend = Accessors.getMethodAccessor(handleMethod);
|
||||
}
|
||||
|
||||
return networkManagerHandle;
|
||||
return networkManagerSend;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,6 +172,27 @@ public final class MinecraftMethods {
|
||||
return networkManagerPacketRead;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the handle/send packet method of network manager.
|
||||
*
|
||||
* @return The handle method.
|
||||
*/
|
||||
public static MethodAccessor getNetworkManagerDisconnectMethod() {
|
||||
if (networkManagerDisconnect == null) {
|
||||
Method handleMethod = FuzzyReflection
|
||||
.fromClass(MinecraftReflection.getNetworkManagerClass(), true)
|
||||
.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.banModifier(Modifier.STATIC)
|
||||
.returnTypeVoid()
|
||||
.parameterCount(1)
|
||||
.parameterExactType(MinecraftReflection.getIChatBaseComponentClass(), 0)
|
||||
.build());
|
||||
networkManagerDisconnect = Accessors.getMethodAccessor(handleMethod);
|
||||
}
|
||||
|
||||
return networkManagerDisconnect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the Packet.read(PacketDataSerializer) method.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -373,6 +373,11 @@ public final class MinecraftReflection {
|
||||
if (clazz == null || object == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check for accidental class objects
|
||||
if (object instanceof Class) {
|
||||
return clazz.isAssignableFrom((Class<?>) object);
|
||||
}
|
||||
|
||||
return clazz.isAssignableFrom(object.getClass());
|
||||
}
|
||||
@@ -502,6 +507,10 @@ public final class MinecraftReflection {
|
||||
return is(getCraftItemStackClass(), obj);
|
||||
}
|
||||
|
||||
public static boolean isIChatBaseComponent(Class<?> target) {
|
||||
return is(getIChatBaseComponentClass(), target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the EntityPlayer (NMS) class.
|
||||
*
|
||||
@@ -676,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");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1889,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;
|
||||
|
||||
@@ -10,7 +10,9 @@ import java.util.Base64;
|
||||
import com.comphenix.protocol.injector.netty.NettyByteBufAdapter;
|
||||
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.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
|
||||
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
|
||||
@@ -31,6 +33,7 @@ public class StreamSerializer {
|
||||
private static final StreamSerializer DEFAULT = new StreamSerializer();
|
||||
|
||||
// Cached methods
|
||||
private static FieldAccessor STREAM_CODEC;
|
||||
private static MethodAccessor READ_ITEM_METHOD;
|
||||
private static MethodAccessor WRITE_ITEM_METHOD;
|
||||
|
||||
@@ -235,14 +238,35 @@ public class StreamSerializer {
|
||||
Object serializer = MinecraftReflection.getPacketDataSerializer(buf);
|
||||
|
||||
if (READ_ITEM_METHOD == null) {
|
||||
READ_ITEM_METHOD = Accessors.getMethodAccessor(FuzzyReflection
|
||||
.fromClass(serializer.getClass(), false)
|
||||
.getMethodByReturnTypeAndParameters("readItemStack", MinecraftReflection.getItemStackClass()));
|
||||
if (MinecraftVersion.v1_21_0.atOrAbove()) {
|
||||
if (STREAM_CODEC == null) {
|
||||
STREAM_CODEC = Accessors.getFieldAccessor(FuzzyReflection
|
||||
.fromClass(MinecraftReflection.getItemStackClass())
|
||||
.getFieldList(FuzzyFieldContract.newBuilder()
|
||||
.typeExact(MinecraftReflection.getStreamCodecClass())
|
||||
.build()).get(1)); //skip OPTIONAL_STREAM_CODEC
|
||||
}
|
||||
|
||||
READ_ITEM_METHOD = Accessors.getMethodAccessor(FuzzyReflection.fromObject(STREAM_CODEC.get(null), true)
|
||||
.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.parameterExactType(MinecraftReflection.getRegistryFriendlyByteBufClass().get())
|
||||
.returnTypeExact(MinecraftReflection.getItemStackClass())
|
||||
.build()));
|
||||
} else {
|
||||
READ_ITEM_METHOD = Accessors.getMethodAccessor(FuzzyReflection
|
||||
.fromClass(serializer.getClass(), false)
|
||||
.getMethodByReturnTypeAndParameters("readItemStack", MinecraftReflection.getItemStackClass()));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// unwrap the item
|
||||
Object nmsItem = READ_ITEM_METHOD.invoke(serializer);
|
||||
Object nmsItem;
|
||||
if (MinecraftVersion.v1_21_0.atOrAbove()) {
|
||||
nmsItem = READ_ITEM_METHOD.invoke(STREAM_CODEC.get(null), serializer);
|
||||
} else {
|
||||
nmsItem = READ_ITEM_METHOD.invoke(serializer);
|
||||
}
|
||||
return nmsItem != null ? MinecraftReflection.getBukkitItemStack(nmsItem) : null;
|
||||
} finally {
|
||||
ReferenceCountUtil.safeRelease(buf);
|
||||
@@ -262,11 +286,26 @@ public class StreamSerializer {
|
||||
* @throws IOException If the operation fails due to reflection problems.
|
||||
*/
|
||||
public void serializeItemStack(DataOutputStream output, ItemStack stack) throws IOException {
|
||||
// TODO this functionality was replaced by the CODEC field in the nms ItemStack
|
||||
if (WRITE_ITEM_METHOD == null) {
|
||||
WRITE_ITEM_METHOD = Accessors.getMethodAccessor(FuzzyReflection
|
||||
.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true)
|
||||
.getMethodByParameters("writeStack", MinecraftReflection.getItemStackClass()));
|
||||
if (MinecraftVersion.v1_21_0.atOrAbove()) {
|
||||
if (STREAM_CODEC == null) {
|
||||
STREAM_CODEC = Accessors.getFieldAccessor(FuzzyReflection
|
||||
.fromClass(MinecraftReflection.getItemStackClass())
|
||||
.getFieldList(FuzzyFieldContract.newBuilder()
|
||||
.typeExact(MinecraftReflection.getStreamCodecClass())
|
||||
.build()).get(1)); //skip OPTIONAL_STREAM_CODEC
|
||||
}
|
||||
|
||||
WRITE_ITEM_METHOD = Accessors.getMethodAccessor(FuzzyReflection.fromObject(STREAM_CODEC.get(null), true)
|
||||
.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.parameterExactArray(MinecraftReflection.getRegistryFriendlyByteBufClass().get(), MinecraftReflection.getItemStackClass())
|
||||
.returnTypeExact(void.class)
|
||||
.build()));
|
||||
} else {
|
||||
WRITE_ITEM_METHOD = Accessors.getMethodAccessor(FuzzyReflection
|
||||
.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true)
|
||||
.getMethodByParameters("writeStack", MinecraftReflection.getItemStackClass()));
|
||||
}
|
||||
}
|
||||
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
@@ -274,7 +313,12 @@ public class StreamSerializer {
|
||||
|
||||
// Get the NMS version of the ItemStack and write it into the buffer
|
||||
Object nmsItem = MinecraftReflection.getMinecraftItemStack(stack);
|
||||
WRITE_ITEM_METHOD.invoke(serializer, nmsItem);
|
||||
|
||||
if (MinecraftVersion.v1_21_0.atOrAbove()) {
|
||||
WRITE_ITEM_METHOD.invoke(STREAM_CODEC.get(null), serializer, nmsItem);
|
||||
} else {
|
||||
WRITE_ITEM_METHOD.invoke(serializer, nmsItem);
|
||||
}
|
||||
|
||||
// write the serialized content to the stream
|
||||
output.write(this.getBytesAndRelease(buf));
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -86,6 +86,15 @@ public class AdventureComponentConverter {
|
||||
return WrappedChatComponent.fromJson(SERIALIZER.serialize(component));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an object that is instance of {@link Component} into a ProtocolLib wrapper
|
||||
* @param component Component
|
||||
* @return ProtocolLib wrapper
|
||||
*/
|
||||
public static WrappedChatComponent fromComponentObject(Object component) {
|
||||
return fromComponent((Component) component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link WrappedComponentStyle} into a {@link Style}
|
||||
* @param wrapper ProtocolLib wrapper
|
||||
|
||||
@@ -11,6 +11,10 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.comphenix.protocol.utility.Validate;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.GameMode;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Protocol;
|
||||
import com.comphenix.protocol.ProtocolLogger;
|
||||
@@ -23,10 +27,6 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.GameMode;
|
||||
|
||||
/**
|
||||
* Represents a generic enum converter.
|
||||
* @author Kristian
|
||||
@@ -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 {
|
||||
@@ -623,6 +637,86 @@ public abstract class EnumWrappers {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the client's intentions when connecting to the server. Previously,
|
||||
* the game utilized the {@code EnumProtocol}, which included the additional states
|
||||
* HANDSHAKE and PLAY. These states are not incorporated in the ClientIntent enum
|
||||
* as they were never valid values for client intent under the current or
|
||||
* past implementations.
|
||||
*
|
||||
* @since 1.20.5
|
||||
*/
|
||||
public enum ClientIntent {
|
||||
STATUS,
|
||||
LOGIN,
|
||||
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;
|
||||
@@ -647,7 +741,12 @@ public abstract class EnumWrappers {
|
||||
private static Class<?> DISPLAY_SLOT_CLASS = null;
|
||||
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;
|
||||
private static Map<Class<?>, EquivalentConverter<?>> FROM_NATIVE = new HashMap<>();
|
||||
private static Map<Class<?>, EquivalentConverter<?>> FROM_WRAPPER = new HashMap<>();
|
||||
@@ -660,116 +759,143 @@ public abstract class EnumWrappers {
|
||||
if (INITIALIZED)
|
||||
return;
|
||||
|
||||
INITIALIZED = true;
|
||||
synchronized (EnumWrappers.class) {
|
||||
// Recheck initialization status inside the lock
|
||||
if (INITIALIZING || INITIALIZED)
|
||||
return;
|
||||
|
||||
PROTOCOL_CLASS = MinecraftReflection.getEnumProtocolClass();
|
||||
CLIENT_COMMAND_CLASS = getEnum(PacketType.Play.Client.CLIENT_COMMAND.getPacketClass(), 0);
|
||||
// Prevent circular calls to initialize during initialization
|
||||
// (certain methods below indirectly call initialize again)
|
||||
INITIALIZING = true;
|
||||
|
||||
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
CHAT_VISIBILITY_CLASS = MinecraftReflection.getMinecraftClass("world.entity.player.EnumChatVisibility", "world.entity.player.ChatVisibility", "world.entity.player.ChatVisiblity"); // Some versions have a typo
|
||||
} else {
|
||||
CHAT_VISIBILITY_CLASS = getEnum(PacketType.Play.Client.SETTINGS.getPacketClass(), 0);
|
||||
}
|
||||
PROTOCOL_CLASS = MinecraftReflection.getEnumProtocolClass();
|
||||
CLIENT_COMMAND_CLASS = getEnum(PacketType.Play.Client.CLIENT_COMMAND.getPacketClass(), 0);
|
||||
|
||||
try {
|
||||
DIFFICULTY_CLASS = getEnum(PacketType.Play.Server.SERVER_DIFFICULTY.getPacketClass(), 0);
|
||||
} catch (Exception ex) {
|
||||
DIFFICULTY_CLASS = getEnum(PacketType.Play.Server.LOGIN.getPacketClass(), 1);
|
||||
}
|
||||
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
CHAT_VISIBILITY_CLASS = MinecraftReflection.getMinecraftClass("world.entity.player.EnumChatVisibility", "world.entity.player.ChatVisibility", "world.entity.player.ChatVisiblity"); // Some versions have a typo
|
||||
} else {
|
||||
CHAT_VISIBILITY_CLASS = getEnum(PacketType.Play.Client.SETTINGS.getPacketClass(), 0);
|
||||
}
|
||||
|
||||
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
GAMEMODE_CLASS = getEnum(MinecraftReflection.getPlayerInfoDataClass(), 0);
|
||||
} else {
|
||||
GAMEMODE_CLASS = getEnum(PacketType.Play.Server.LOGIN.getPacketClass(), 0);
|
||||
}
|
||||
try {
|
||||
DIFFICULTY_CLASS = getEnum(PacketType.Play.Server.SERVER_DIFFICULTY.getPacketClass(), 0);
|
||||
} catch (Exception ex) {
|
||||
DIFFICULTY_CLASS = getEnum(PacketType.Play.Server.LOGIN.getPacketClass(), 1);
|
||||
}
|
||||
|
||||
RESOURCE_PACK_STATUS_CLASS = getEnum(PacketType.Play.Client.RESOURCE_PACK_STATUS.getPacketClass(), 0);
|
||||
TITLE_ACTION_CLASS = getEnum(PacketType.Play.Server.TITLE.getPacketClass(), 0);
|
||||
WORLD_BORDER_ACTION_CLASS = getEnum(PacketType.Play.Server.WORLD_BORDER.getPacketClass(), 0);
|
||||
COMBAT_EVENT_TYPE_CLASS = getEnum(PacketType.Play.Server.COMBAT_EVENT.getPacketClass(), 0);
|
||||
PLAYER_DIG_TYPE_CLASS = getEnum(PacketType.Play.Client.BLOCK_DIG.getPacketClass(), 1);
|
||||
PLAYER_ACTION_CLASS = getEnum(PacketType.Play.Client.ENTITY_ACTION.getPacketClass(), 0);
|
||||
SCOREBOARD_ACTION_CLASS = getEnum(PacketType.Play.Server.SCOREBOARD_SCORE.getPacketClass(), 0);
|
||||
PARTICLE_CLASS = getEnum(PacketType.Play.Server.WORLD_PARTICLES.getPacketClass(), 0);
|
||||
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
GAMEMODE_CLASS = getEnum(MinecraftReflection.getPlayerInfoDataClass(), 0);
|
||||
} else {
|
||||
GAMEMODE_CLASS = getEnum(PacketType.Play.Server.LOGIN.getPacketClass(), 0);
|
||||
}
|
||||
|
||||
PLAYER_INFO_ACTION_CLASS = getEnum(PacketType.Play.Server.PLAYER_INFO.getPacketClass(), 0);
|
||||
if (PLAYER_INFO_ACTION_CLASS == null) {
|
||||
// todo: we can also use getField(0).getGenericType().getTypeParameters()[0]; but this should hold for now
|
||||
PLAYER_INFO_ACTION_CLASS = PacketType.Play.Server.PLAYER_INFO.getPacketClass().getClasses()[1];
|
||||
}
|
||||
RESOURCE_PACK_STATUS_CLASS = getEnum(PacketType.Play.Client.RESOURCE_PACK_STATUS.getPacketClass(), 0);
|
||||
TITLE_ACTION_CLASS = getEnum(PacketType.Play.Server.TITLE.getPacketClass(), 0);
|
||||
WORLD_BORDER_ACTION_CLASS = getEnum(PacketType.Play.Server.WORLD_BORDER.getPacketClass(), 0);
|
||||
COMBAT_EVENT_TYPE_CLASS = getEnum(PacketType.Play.Server.COMBAT_EVENT.getPacketClass(), 0);
|
||||
PLAYER_DIG_TYPE_CLASS = getEnum(PacketType.Play.Client.BLOCK_DIG.getPacketClass(), 1);
|
||||
PLAYER_ACTION_CLASS = getEnum(PacketType.Play.Client.ENTITY_ACTION.getPacketClass(), 0);
|
||||
SCOREBOARD_ACTION_CLASS = getEnum(PacketType.Play.Server.SCOREBOARD_SCORE.getPacketClass(), 0);
|
||||
PARTICLE_CLASS = getEnum(PacketType.Play.Server.WORLD_PARTICLES.getPacketClass(), 0);
|
||||
|
||||
try {
|
||||
SOUND_CATEGORY_CLASS = MinecraftReflection.getMinecraftClass("sounds.SoundCategory");
|
||||
} catch (Exception ex) {
|
||||
SOUND_CATEGORY_CLASS = getEnum(PacketType.Play.Server.NAMED_SOUND_EFFECT.getPacketClass(), 0);
|
||||
}
|
||||
PLAYER_INFO_ACTION_CLASS = getEnum(PacketType.Play.Server.PLAYER_INFO.getPacketClass(), 0);
|
||||
if (PLAYER_INFO_ACTION_CLASS == null) {
|
||||
// todo: we can also use getField(0).getGenericType().getTypeParameters()[0]; but this should hold for now
|
||||
PLAYER_INFO_ACTION_CLASS = PacketType.Play.Server.PLAYER_INFO.getPacketClass().getClasses()[1];
|
||||
}
|
||||
|
||||
try {
|
||||
// TODO enum names are more stable than their packet associations
|
||||
ITEM_SLOT_CLASS = MinecraftReflection.getMinecraftClass("world.entity.EnumItemSlot", "world.entity.EquipmentSlot", "EnumItemSlot");
|
||||
} catch (Exception ex) {
|
||||
ITEM_SLOT_CLASS = getEnum(PacketType.Play.Server.ENTITY_EQUIPMENT.getPacketClass(), 0);
|
||||
}
|
||||
try {
|
||||
SOUND_CATEGORY_CLASS = MinecraftReflection.getMinecraftClass("sounds.SoundCategory");
|
||||
} catch (Exception ex) {
|
||||
SOUND_CATEGORY_CLASS = getEnum(PacketType.Play.Server.NAMED_SOUND_EFFECT.getPacketClass(), 0);
|
||||
}
|
||||
|
||||
// In 1.17 the hand and use action class is no longer a field in the packet
|
||||
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
|
||||
HAND_CLASS = MinecraftReflection.getMinecraftClass("world.EnumHand", "world.InteractionHand");
|
||||
try {
|
||||
// TODO enum names are more stable than their packet associations
|
||||
ITEM_SLOT_CLASS = MinecraftReflection.getMinecraftClass("world.entity.EnumItemSlot", "world.entity.EquipmentSlot", "EnumItemSlot");
|
||||
} catch (Exception ex) {
|
||||
ITEM_SLOT_CLASS = getEnum(PacketType.Play.Server.ENTITY_EQUIPMENT.getPacketClass(), 0);
|
||||
}
|
||||
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getEnumEntityUseActionClass(), true);
|
||||
Method getType = fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.parameterCount(0)
|
||||
.returnTypeMatches(FuzzyMatchers.except(Void.class))
|
||||
.build());
|
||||
// In 1.17 the hand and use action class is no longer a field in the packet
|
||||
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
|
||||
HAND_CLASS = MinecraftReflection.getMinecraftClass("world.EnumHand", "world.InteractionHand");
|
||||
|
||||
ENTITY_USE_ACTION_CLASS = getType.getReturnType();
|
||||
} else {
|
||||
HAND_CLASS = getEnum(PacketType.Play.Client.USE_ENTITY.getPacketClass(), 1);
|
||||
ENTITY_USE_ACTION_CLASS = getEnum(PacketType.Play.Client.USE_ENTITY.getPacketClass(), 0);
|
||||
}
|
||||
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getEnumEntityUseActionClass(), true);
|
||||
Method getType = fuzzy.getMethod(FuzzyMethodContract.newBuilder()
|
||||
.parameterCount(0)
|
||||
.returnTypeMatches(FuzzyMatchers.except(Void.class))
|
||||
.build());
|
||||
|
||||
// 1.19 removed the entity spawn packet and moved the direction into a seperated class
|
||||
if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
|
||||
DIRECTION_CLASS = MinecraftReflection.getMinecraftClass("core.EnumDirection", "core.Direction");
|
||||
} else {
|
||||
DIRECTION_CLASS = getEnum(PacketType.Play.Server.SPAWN_ENTITY_PAINTING.getPacketClass(), 0);
|
||||
}
|
||||
ENTITY_USE_ACTION_CLASS = getType.getReturnType();
|
||||
} else {
|
||||
HAND_CLASS = getEnum(PacketType.Play.Client.USE_ENTITY.getPacketClass(), 1);
|
||||
ENTITY_USE_ACTION_CLASS = getEnum(PacketType.Play.Client.USE_ENTITY.getPacketClass(), 0);
|
||||
}
|
||||
|
||||
CHAT_TYPE_CLASS = getEnum(PacketType.Play.Server.CHAT.getPacketClass(), 0);
|
||||
ENTITY_POSE_CLASS = MinecraftReflection.getNullableNMS("world.entity.EntityPose", "world.entity.Pose", "EntityPose");
|
||||
DISPLAY_SLOT_CLASS = MinecraftReflection.getNullableNMS("world.scores.DisplaySlot");
|
||||
// 1.19 removed the entity spawn packet and moved the direction into a seperated class
|
||||
if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
|
||||
DIRECTION_CLASS = MinecraftReflection.getMinecraftClass("core.EnumDirection", "core.Direction");
|
||||
} else {
|
||||
DIRECTION_CLASS = getEnum(PacketType.Play.Server.SPAWN_ENTITY_PAINTING.getPacketClass(), 0);
|
||||
}
|
||||
|
||||
RENDER_TYPE_CLASS = MinecraftReflection.getNullableNMS(
|
||||
"world.scores.criteria.ObjectiveCriteria$RenderType",
|
||||
"world.scores.criteria.IScoreboardCriteria$EnumScoreboardHealthDisplay",
|
||||
"IScoreboardCriteria$EnumScoreboardHealthDisplay");
|
||||
CHAT_FORMATTING_CLASS = MinecraftReflection.getNullableNMS("ChatFormatting", "EnumChatFormat");
|
||||
CHAT_TYPE_CLASS = getEnum(PacketType.Play.Server.CHAT.getPacketClass(), 0);
|
||||
ENTITY_POSE_CLASS = MinecraftReflection.getNullableNMS("world.entity.EntityPose", "world.entity.Pose", "EntityPose");
|
||||
DISPLAY_SLOT_CLASS = MinecraftReflection.getNullableNMS("world.scores.DisplaySlot");
|
||||
|
||||
associate(PROTOCOL_CLASS, Protocol.class, getProtocolConverter());
|
||||
associate(CLIENT_COMMAND_CLASS, ClientCommand.class, getClientCommandConverter());
|
||||
associate(CHAT_VISIBILITY_CLASS, ChatVisibility.class, getChatVisibilityConverter());
|
||||
associate(DIFFICULTY_CLASS, Difficulty.class, getDifficultyConverter());
|
||||
associate(GAMEMODE_CLASS, NativeGameMode.class, getGameModeConverter());
|
||||
associate(RESOURCE_PACK_STATUS_CLASS, ResourcePackStatus.class, getResourcePackStatusConverter());
|
||||
associate(PLAYER_INFO_ACTION_CLASS, PlayerInfoAction.class, getPlayerInfoActionConverter());
|
||||
associate(TITLE_ACTION_CLASS, TitleAction.class, getTitleActionConverter());
|
||||
associate(WORLD_BORDER_ACTION_CLASS, WorldBorderAction.class, getWorldBorderActionConverter());
|
||||
associate(COMBAT_EVENT_TYPE_CLASS, CombatEventType.class, getCombatEventTypeConverter());
|
||||
associate(PLAYER_DIG_TYPE_CLASS, PlayerDigType.class, getPlayerDiggingActionConverter());
|
||||
associate(PLAYER_ACTION_CLASS, PlayerAction.class, getEntityActionConverter());
|
||||
associate(SCOREBOARD_ACTION_CLASS, ScoreboardAction.class, getUpdateScoreActionConverter());
|
||||
associate(PARTICLE_CLASS, Particle.class, getParticleConverter());
|
||||
associate(SOUND_CATEGORY_CLASS, SoundCategory.class, getSoundCategoryConverter());
|
||||
associate(ITEM_SLOT_CLASS, ItemSlot.class, getItemSlotConverter());
|
||||
associate(DIRECTION_CLASS, Direction.class, getDirectionConverter());
|
||||
associate(CHAT_TYPE_CLASS, ChatType.class, getChatTypeConverter());
|
||||
associate(HAND_CLASS, Hand.class, getHandConverter());
|
||||
associate(ENTITY_USE_ACTION_CLASS, EntityUseAction.class, getEntityUseActionConverter());
|
||||
associate(DISPLAY_SLOT_CLASS, DisplaySlot.class, getDisplaySlotConverter());
|
||||
associate(RENDER_TYPE_CLASS, RenderType.class, getRenderTypeConverter());
|
||||
associate(CHAT_FORMATTING_CLASS, ChatFormatting.class, getChatFormattingConverter());
|
||||
RENDER_TYPE_CLASS = MinecraftReflection.getNullableNMS(
|
||||
"world.scores.criteria.ObjectiveCriteria$RenderType",
|
||||
"world.scores.criteria.IScoreboardCriteria$EnumScoreboardHealthDisplay",
|
||||
"IScoreboardCriteria$EnumScoreboardHealthDisplay");
|
||||
CHAT_FORMATTING_CLASS = MinecraftReflection.getNullableNMS("ChatFormatting", "EnumChatFormat");
|
||||
|
||||
if (ENTITY_POSE_CLASS != null) {
|
||||
associate(ENTITY_POSE_CLASS, EntityPose.class, getEntityPoseConverter());
|
||||
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());
|
||||
associate(DIFFICULTY_CLASS, Difficulty.class, getDifficultyConverter());
|
||||
associate(GAMEMODE_CLASS, NativeGameMode.class, getGameModeConverter());
|
||||
associate(RESOURCE_PACK_STATUS_CLASS, ResourcePackStatus.class, getResourcePackStatusConverter());
|
||||
associate(PLAYER_INFO_ACTION_CLASS, PlayerInfoAction.class, getPlayerInfoActionConverter());
|
||||
associate(TITLE_ACTION_CLASS, TitleAction.class, getTitleActionConverter());
|
||||
associate(WORLD_BORDER_ACTION_CLASS, WorldBorderAction.class, getWorldBorderActionConverter());
|
||||
associate(COMBAT_EVENT_TYPE_CLASS, CombatEventType.class, getCombatEventTypeConverter());
|
||||
associate(PLAYER_DIG_TYPE_CLASS, PlayerDigType.class, getPlayerDiggingActionConverter());
|
||||
associate(PLAYER_ACTION_CLASS, PlayerAction.class, getEntityActionConverter());
|
||||
associate(SCOREBOARD_ACTION_CLASS, ScoreboardAction.class, getUpdateScoreActionConverter());
|
||||
associate(PARTICLE_CLASS, Particle.class, getParticleConverter());
|
||||
associate(SOUND_CATEGORY_CLASS, SoundCategory.class, getSoundCategoryConverter());
|
||||
associate(ITEM_SLOT_CLASS, ItemSlot.class, getItemSlotConverter());
|
||||
associate(DIRECTION_CLASS, Direction.class, getDirectionConverter());
|
||||
associate(CHAT_TYPE_CLASS, ChatType.class, getChatTypeConverter());
|
||||
associate(HAND_CLASS, Hand.class, getHandConverter());
|
||||
associate(ENTITY_USE_ACTION_CLASS, EntityUseAction.class, getEntityUseActionConverter());
|
||||
associate(DISPLAY_SLOT_CLASS, DisplaySlot.class, getDisplaySlotConverter());
|
||||
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());
|
||||
}
|
||||
|
||||
INITIALIZED = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -933,6 +1059,26 @@ public abstract class EnumWrappers {
|
||||
return CHAT_FORMATTING_CLASS;
|
||||
}
|
||||
|
||||
public static Class<?> getClientIntentClass() {
|
||||
initialize();
|
||||
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);
|
||||
@@ -1026,6 +1172,22 @@ public abstract class EnumWrappers {
|
||||
return new EnumConverter<>(getChatFormattingClass(), ChatFormatting.class);
|
||||
}
|
||||
|
||||
public static EquivalentConverter<ClientIntent> getClientIntentConverter() {
|
||||
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,15 +110,16 @@ 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;
|
||||
|
||||
private Builder(@Nullable WrappedTeamParameters template) {
|
||||
if (template != null) {
|
||||
this.displayName = template.getDisplayName();
|
||||
this.prefix = template.getDisplayName();
|
||||
this.suffix = template.getDisplayName();
|
||||
this.prefix = template.getPrefix();
|
||||
this.suffix = template.getSuffix();
|
||||
this.nametagVisibility = template.getNametagVisibility();
|
||||
this.collisionRule = template.getCollisionRule();
|
||||
this.color = template.getColor();
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,45 +1,64 @@
|
||||
package com.comphenix.protocol;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
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.Commands.CommandSelection;
|
||||
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.RegistryDataLoader;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.Bootstrap;
|
||||
import net.minecraft.server.RegistryLayer;
|
||||
import net.minecraft.server.ReloadableServerResources;
|
||||
import net.minecraft.server.WorldLoader;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
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.CloseableResourceManager;
|
||||
import net.minecraft.server.packs.resources.MultiPackResourceManager;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.tags.TagKey;
|
||||
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.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.util.CraftMagicNumbers;
|
||||
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;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
@@ -81,8 +100,6 @@ public class BukkitInitialization {
|
||||
synchronized (initLock) {
|
||||
if (initialized) {
|
||||
return;
|
||||
} else {
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -95,36 +112,42 @@ public class BukkitInitialization {
|
||||
instance.setPackage();
|
||||
|
||||
// Minecraft Data Init
|
||||
SharedConstants.tryDetectVersion();
|
||||
Bootstrap.bootStrap();
|
||||
SharedConstants.tryDetectVersion(); // .tryDetectVersion()
|
||||
Bootstrap.bootStrap(); // .bootStrap()
|
||||
|
||||
PackRepository resourcePackRepository = ServerPacksSource.createVanillaTrustedRepository();
|
||||
resourcePackRepository.reload();
|
||||
PackRepository repo = ServerPacksSource.createVanillaTrustedRepository();
|
||||
repo.reload();
|
||||
|
||||
CloseableResourceManager resourceManager = new MultiPackResourceManager(
|
||||
ResourceManager resourceManager = new MultiPackResourceManager(
|
||||
PackType.SERVER_DATA,
|
||||
resourcePackRepository.getAvailablePacks().stream().map(Pack::open).collect(Collectors.toList())
|
||||
repo.getAvailablePacks().stream().map(Pack::open).collect(Collectors.toList())
|
||||
);
|
||||
LayeredRegistryAccess<RegistryLayer> layeredRegistryAccess = RegistryLayer.createRegistryAccess();
|
||||
layeredRegistryAccess = WorldLoader.loadAndReplaceLayer(resourceManager, layeredRegistryAccess, RegistryLayer.WORLDGEN, RegistryDataLoader.WORLDGEN_REGISTRIES);
|
||||
RegistryAccess.Frozen registryCustom = layeredRegistryAccess.compositeAccess().freeze();
|
||||
|
||||
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,
|
||||
layeredRegistryAccess,
|
||||
FeatureFlags.REGISTRY.allFlags(),
|
||||
CommandSelection.DEDICATED,
|
||||
layeredAccess2,
|
||||
tags,
|
||||
FeatureFlags.REGISTRY.allFlags() /* REGISTRY.allFlags() */,
|
||||
Commands.CommandSelection.DEDICATED /* DEDICATED */,
|
||||
0,
|
||||
MoreExecutors.directExecutor(),
|
||||
MoreExecutors.directExecutor()
|
||||
).join();
|
||||
dataPackResources.updateRegistryTags();
|
||||
dataPackResources.updateStaticRegistryTags();
|
||||
|
||||
try {
|
||||
RegistryAccess.class.getName();
|
||||
/* try {
|
||||
IRegistry.class.getName();
|
||||
} catch (Throwable ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
} */
|
||||
|
||||
String releaseTarget = MinecraftReflectionTestUtil.RELEASE_TARGET;
|
||||
String serverVersion = CraftServer.class.getPackage().getImplementationVersion();
|
||||
@@ -148,15 +171,56 @@ public class BukkitInitialization {
|
||||
NamespacedKey key = invocation.getArgument(0);
|
||||
return new CraftLootTable(key, dataPackResources.fullRegistries().getLootTable(CraftLootTable.bukkitKeyToMinecraft(key)));
|
||||
});
|
||||
|
||||
when(mockedServer.getRegistry(any())).thenAnswer(invocation -> {
|
||||
Class<? extends Keyed> registryType = invocation.getArgument(0);
|
||||
Class<Keyed> registryType = invocation.getArgument(0);
|
||||
Object registry = CraftRegistry.createRegistry(registryType, registryCustom);
|
||||
|
||||
try {
|
||||
return CraftRegistry.createRegistry(registryType, registryCustom);
|
||||
} catch (Exception ignored) {
|
||||
return mock(org.bukkit.Registry.class);
|
||||
if (registry == null) {
|
||||
System.err.println("WARN: Missing registry for " + registryType);
|
||||
return new DummyRegistry<>();
|
||||
}
|
||||
|
||||
return registry;
|
||||
});
|
||||
|
||||
when(mockedServer.getTag(any(), any(), any())).then(mock -> {
|
||||
String registry = mock.getArgument(0);
|
||||
Class<?> clazz = mock.getArgument(2);
|
||||
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.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.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<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<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();
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
ServerLevel nmsWorld = mock(ServerLevel.class);
|
||||
@@ -181,7 +245,8 @@ public class BukkitInitialization {
|
||||
|
||||
// Init Enchantments
|
||||
Enchantments.AQUA_AFFINITY.getClass();
|
||||
// Enchantment.stopAcceptingRegistrations();
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,4 +267,28 @@ public class BukkitInitialization {
|
||||
MinecraftReflectionTestUtil.init();
|
||||
}
|
||||
}
|
||||
|
||||
class DummyRegistry<T extends Keyed> implements Registry<T> {
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return Collections.emptyIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(NamespacedKey key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getOrThrow(@NotNull NamespacedKey namespacedKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<T> stream() {
|
||||
List<T> empty = Collections.emptyList();
|
||||
return empty.stream();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,22 +14,28 @@
|
||||
*/
|
||||
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;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
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.ServerboundHelloPacket;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* @author dmulloy2
|
||||
@@ -55,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) -> {
|
||||
@@ -249,6 +254,16 @@ 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() {
|
||||
@@ -280,6 +295,24 @@ public class PacketTypeTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled // TODO -- lots of constructor parameters :(
|
||||
public void testCreateMapChunk() {
|
||||
new PacketContainer(PacketType.Play.Server.MAP_CHUNK);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled // TODO -- ScoreboardObjective parameter in constructor is causing this to fail
|
||||
public void testCreateScoreboardObjective() {
|
||||
new PacketContainer(PacketType.Play.Server.SCOREBOARD_OBJECTIVE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled // TODO -- Entity parameter in constructor is causing this to fail
|
||||
public void testCreateEntitySound() {
|
||||
new PacketContainer(PacketType.Play.Server.ENTITY_SOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPacketCreation() {
|
||||
List<PacketType> failed = new ArrayList<>();
|
||||
@@ -288,6 +321,12 @@ public class PacketTypeTest {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type == PacketType.Play.Server.ENTITY_SOUND
|
||||
|| type == PacketType.Play.Server.SCOREBOARD_OBJECTIVE
|
||||
|| type == PacketType.Play.Server.MAP_CHUNK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
new PacketContainer(type);
|
||||
} catch (Exception ex) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Loaded 100 of 156 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user