Compare commits

..
2 Commits
Author SHA1 Message Date
Dan Mulloy 4649daef82 fix: fail gracefully for unknown protocol 2024-06-14 13:59:53 -05:00
Dan Mulloy 70d47ce622 wip: mojang mappings 2024-06-13 20:17:15 -05:00
127 changed files with 3454 additions and 3444 deletions

No files matched your search

+5 -7
View File
@@ -1,20 +1,18 @@
name: ProtocolLib full build lifecycle
on:
push:
branches-ignore:
- 'gh-pages'
pull_request:
- push
- pull_request
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Setup java
uses: actions/setup-java@v4
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21'
@@ -24,7 +22,7 @@ jobs:
run: ./gradlew build shadowJar --no-daemon
- name: Upload plugin file
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v2
with:
name: ProtocolLib
path: build/libs/ProtocolLib.jar
+4 -4
View File
@@ -20,17 +20,17 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Setup java
uses: actions/setup-java@v4
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21'
cache: 'gradle'
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v2
with:
languages: 'java'
@@ -38,4 +38,4 @@ jobs:
run: ./gradlew build -x test --no-daemon
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v2
+1
View File
@@ -181,3 +181,4 @@ logs/
build/
logs/
.java-version
gradle.properties
+15 -9
View File
@@ -2,7 +2,7 @@
Certain tasks are impossible to perform with the standard Bukkit API, and may require
working with and even modifying Minecraft directly. A common technique is to modify incoming
and outgoing packets, or inject custom packets into the
and outgoing [packets](https://wiki.vg/Protocol), 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,10 +12,9 @@ lead to more subtle bugs.
### Resources
* [Spigot Page](https://spigotmc.org/resources/protocollib.1997/)
* [Releases](https://github.com/dmulloy2/ProtocolLib/releases)
* [JavaDoc](https://dmulloy2.net/ProtocolLib/javadoc/)
* [Protocol Wiki](https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/Protocol)
* [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)
### Compilation
@@ -43,11 +42,18 @@ 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>net.dmulloy2</groupId>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>5.4.0</version>
<version>5.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
@@ -57,11 +63,11 @@ Or use the maven dependency with gradle:
```gradle
repositories {
mavenCentral()
maven { url "https://repo.dmulloy2.net/repository/public/" }
}
dependencies {
compileOnly 'net.dmulloy2:ProtocolLib:5.4.0'
compileOnly 'com.comphenix.protocol:ProtocolLib:5.1.0'
}
```
+130
View File
@@ -0,0 +1,130 @@
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'
}
-169
View File
@@ -1,169 +0,0 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import io.github.patrick.gradle.remapper.RemapTask
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
plugins {
`java-library`
`maven-publish`
`signing`
id("com.gradleup.shadow") version "9.0.0-beta13"
id("io.github.patrick.remapper") version "1.4.2"
id("com.vanniktech.maven.publish") version "0.34.0"
}
group = "net.dmulloy2"
description = "Provides access to the Minecraft protocol"
val mcVersion = "1.21.8"
val isSnapshot = version.toString().endsWith("-SNAPSHOT")
val buildNumber = System.getenv("BUILD_NUMBER") ?: ""
val isJenkins = buildNumber.isNotEmpty()
repositories {
if (!isJenkins) {
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")
/*
* TODO(fix): once you update kyori:adventure-text-serializer-gson please uncomment the TODO in
* com.comphenix.protocol.wrappers.WrappedComponentStyleTest if the following issue got fixed:
* https://github.com/KyoriPowered/adventure/issues/1194
*/
compileOnly("net.kyori:adventure-text-serializer-gson:4.21.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:${mcVersion}-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_17
targetCompatibility = JavaVersion.VERSION_17
}
tasks {
processResources {
val fullVersion = if (isSnapshot && isJenkins) "${version}-${buildNumber}" 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"
}
remap {
dependsOn("shadowJar")
inputTask.set(getByName<ShadowJar>("shadowJar"))
version.set(mcVersion)
action.set(RemapTask.Action.MOJANG_TO_SPIGOT)
}
assemble {
dependsOn("remap")
}
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")
}
}
}
+2 -6
View File
@@ -1,6 +1,2 @@
version=5.4.0
mavenCentralUsername=
mavenCentralPassword=
signing.keyId=
signing.password=
signing.secretKeyRingFile=
nexusUsername=""
nexusPassword=""
@@ -14,18 +14,18 @@ import java.util.Map;
import java.util.UUID;
import java.util.function.BiConsumer;
import org.apache.commons.lang.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 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 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 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,92 +155,84 @@ 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 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");
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
/**
* @deprecated Removed in 1.9
@@ -260,6 +252,8 @@ 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}
*/
@@ -284,6 +278,8 @@ 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}
*/
@@ -291,6 +287,8 @@ 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
*/
@@ -405,19 +403,6 @@ 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
@@ -440,70 +425,66 @@ 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 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");
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");
/**
* @deprecated Removed in 1.17
@@ -632,7 +613,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, "LoginFinished", "GameProfile", "Success", "SPacketLoginSuccess");
public static final PacketType SUCCESS = new PacketType(PROTOCOL, SENDER, 0x02, "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");
@@ -711,8 +692,6 @@ 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
@@ -744,7 +723,6 @@ 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();
@@ -504,17 +504,6 @@ 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;
@@ -17,7 +17,9 @@ 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 org.bukkit.plugin.Plugin;
@@ -35,12 +37,17 @@ public class ProtocolLibrary {
/**
* The maximum version ProtocolLib has been tested with.
*/
public static final String MAXIMUM_MINECRAFT_VERSION = "1.21.8";
public static final String MAXIMUM_MINECRAFT_VERSION = "1.20.4";
/**
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.21.8) was released.
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.20.4) was released.
*/
public static final String MINECRAFT_LAST_RELEASE_DATE = "2025-07-17";
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");
private static Plugin plugin;
private static ProtocolConfig config;
@@ -17,6 +17,14 @@
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;
@@ -29,16 +37,8 @@ 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 : ImmutableList.copyOf(processingQueue.values())) {
for (AsyncListenerHandler listener : processingQueue.values()) {
// Remove the listener
if (Objects.equal(listener.getPlugin(), plugin)) {
unregisterAsyncHandler(listener);
@@ -607,6 +607,8 @@ 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);
@@ -0,0 +1,494 @@
/*
* 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);
}
@@ -0,0 +1,266 @@
/*
* 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;
}
}
@@ -30,12 +30,8 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.ProtocolConfig;
import com.comphenix.protocol.ProtocolLib;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.collections.ExpireHashMap;
@@ -45,6 +41,11 @@ 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;
/**
* Internal class used to handle exceptions.
*
@@ -478,7 +479,7 @@ public class DetailedErrorReporter implements ErrorReporter {
} catch (LinkageError ex) {
// Apache is probably missing
apacheCommonsMissing = true;
} catch (OutOfMemoryError e) {
} catch (ThreadDeath | 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)
@@ -1,18 +1,17 @@
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.WrappedProfilePublicKey.WrappedProfileKeyData;
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 org.bukkit.Material;
import org.bukkit.Sound;
@@ -26,49 +25,10 @@ import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.utility.StreamSerializer;
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 javax.annotation.Nonnull;
import java.lang.reflect.Array;
import java.time.Instant;
import java.util.*;
public abstract class AbstractStructure {
protected transient Object handle;
@@ -1174,17 +1134,6 @@ 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,7 +13,6 @@ 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,12 +17,7 @@
package com.comphenix.protocol.events;
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 java.util.*;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.injector.GamePhase;
@@ -95,6 +90,20 @@ 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.
*
@@ -302,7 +311,6 @@ public class ListeningWhitelist {
* @param gamePhase - the gamephase.
* @return This builder, for chaining.
*/
@Deprecated
public Builder gamePhase(GamePhase gamePhase) {
this.gamePhase = gamePhase;
return this;
@@ -313,7 +321,6 @@ public class ListeningWhitelist {
*
* @return This builder, for chaining.
*/
@Deprecated
public Builder gamePhaseBoth() {
return gamePhase(GamePhase.BOTH);
}
@@ -152,7 +152,6 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
return lastSeen;
}
@Override
public Location getRespawnLocation() {
return null;
}
@@ -241,11 +240,21 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
return uuid;
}
/**
* @return
*/
@NotNull
@Override
public @NotNull PlayerProfile getPlayerProfile() {
public PlayerProfile getPlayerProfile() {
return null;
}
/*
public com.destroystokyo.paper.profile.PlayerProfile getPlayerProfile() {
return null;
}
*/
@Override
public String getName() {
return name;
@@ -288,6 +297,11 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
return online;
}
// @Override
public boolean isConnected() {
return false;
}
@Override
public boolean isWhitelisted() {
return whitelisted;
@@ -22,7 +22,6 @@ package com.comphenix.protocol.injector;
*
* @author Kristian
*/
@Deprecated
public enum GamePhase {
/**
* Only listen for packets sent or received before a player has logged in.
@@ -25,7 +25,7 @@ import com.comphenix.protocol.events.PacketEvent;
*
* @author Kristian
*/
public interface ListenerManager {
public interface ListenerInvoker {
boolean hasInboundListener(PacketType packetType);
@@ -38,12 +38,24 @@ public interface ListenerManager {
*
* @param event - the packet event to invoke.
*/
void invokeInboundPacketListeners(PacketEvent event);
void invokePacketReceiving(PacketEvent event);
/**
* Invokes the given packet event for every registered listener.
*
* @param event - the packet event to invoke.
*/
void invokeOutboundPacketListeners(PacketEvent event);
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);
}
@@ -1,7 +1,5 @@
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;
@@ -9,6 +7,7 @@ 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.
@@ -44,7 +43,7 @@ public class NetworkProcessor {
for (PacketPostListener listener : marker.getPostListeners()) {
try {
listener.onPostEvent(event);
} catch (OutOfMemoryError e) {
} catch (OutOfMemoryError | ThreadDeath e) {
throw e;
} catch (Throwable e) {
this.reporter.reportMinimal(listener.getPlugin(), "SentListener.run()", e);
@@ -172,6 +172,8 @@ public class PacketConstructor {
result = unwrapper.unwrapItem(values[i]);
} catch (OutOfMemoryError e) {
throw e;
} catch (ThreadDeath e) {
throw e;
} catch (Throwable e) {
lastException = e;
}
@@ -46,10 +46,15 @@ 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;
public class PacketFilterManager implements ListenerManager, InternalManager {
import io.netty.channel.Channel;
public class PacketFilterManager implements ListenerInvoker, InternalManager {
// plugin verifier reports
private static final ReportType PLUGIN_VERIFIER_ERROR = new ReportType("Plugin verifier error: %s");
@@ -75,6 +80,7 @@ public class PacketFilterManager implements ListenerManager, InternalManager {
private final Set<PacketListener> registeredListeners;
// injectors
private final PlayerInjectionHandler playerInjectionHandler;
private final NetworkManagerInjector networkManagerInjector;
// status of this manager
@@ -105,8 +111,10 @@ public class PacketFilterManager implements ListenerManager, 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();
@@ -114,7 +122,7 @@ public class PacketFilterManager implements ListenerManager, InternalManager {
// hook into all connected players
for (Player player : this.server.getOnlinePlayers()) {
this.networkManagerInjector.getInjector(player).inject();
this.playerInjectionHandler.injectPlayer(player, ConflictStrategy.BAIL_OUT);
}
}
@@ -172,7 +180,7 @@ public class PacketFilterManager implements ListenerManager, InternalManager {
}
// process outbound
this.networkManagerInjector.getInjector(receiver).sendClientboundPacket(packet.getHandle(), marker, filters);
this.playerInjectionHandler.sendServerPacket(receiver, packet, marker, filters);
}
}
@@ -184,7 +192,13 @@ public class PacketFilterManager implements ListenerManager, InternalManager {
@Override
public void sendWirePacket(Player receiver, WirePacket packet) {
if (!this.closed) {
this.networkManagerInjector.getInjector(receiver).sendWirePacket(packet);
// 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);
}
}
@@ -213,7 +227,7 @@ public class PacketFilterManager implements ListenerManager, InternalManager {
if (filters) {
// post to all listeners
PacketEvent event = PacketEvent.fromClient(this.networkManagerInjector, packet, null, sender);
this.invokeInboundPacketListeners(event);
this.invokePacketReceiving(event);
if (event.isCancelled()) {
return;
}
@@ -226,13 +240,13 @@ public class PacketFilterManager implements ListenerManager, InternalManager {
}
// post to the player inject, reset our cancel state change
this.networkManagerInjector.getInjector(sender).readServerboundPacket(nmsPacket);
this.playerInjectionHandler.receiveClientPacket(sender, nmsPacket);
}
}
@Override
public int getProtocolVersion(Player player) {
return this.networkManagerInjector.getInjector(player).getProtocolVersion();
return this.playerInjectionHandler.getProtocolVersion(player);
}
@Override
@@ -433,12 +447,12 @@ public class PacketFilterManager implements ListenerManager, InternalManager {
@EventHandler(priority = EventPriority.LOWEST)
public void handleLogin(PlayerLoginEvent event) {
networkManagerInjector.getInjector(event.getPlayer()).inject();
PacketFilterManager.this.playerInjectionHandler.updatePlayer(event.getPlayer());
}
@EventHandler(priority = EventPriority.LOWEST)
public void handleJoin(PlayerJoinEvent event) {
networkManagerInjector.getInjector(event.getPlayer()).inject();
PacketFilterManager.this.playerInjectionHandler.updatePlayer(event.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR)
@@ -504,19 +518,33 @@ public class PacketFilterManager implements ListenerManager, InternalManager {
}
@Override
public void invokeInboundPacketListeners(PacketEvent event) {
public void invokePacketReceiving(PacketEvent event) {
if (!this.closed) {
this.postPacketToListeners(this.inboundListeners, event, false);
}
}
@Override
public void invokeOutboundPacketListeners(PacketEvent event) {
public void invokePacketSending(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,6 +1,5 @@
package com.comphenix.protocol.injector.collection;
import java.util.Iterator;
import java.util.Set;
import javax.annotation.Nullable;
@@ -46,9 +45,7 @@ public abstract class PacketListenerSet {
public void addListener(PacketListener packetListener) {
ListeningWhitelist listeningWhitelist = getListeningWhitelist(packetListener);
for (Iterator<PacketType> packet = listeningWhitelist.getTypes().iterator(); packet.hasNext();) {
PacketType packetType = packet.next();
for (PacketType packetType : listeningWhitelist.getTypes()) {
Set<PacketType> supportedPacketTypes = (packetType.getSender() == Sender.SERVER)
? PacketRegistry.getServerPacketTypes()
: PacketRegistry.getClientPacketTypes();
@@ -59,7 +56,7 @@ public abstract class PacketListenerSet {
.build());
// remove unknown packet types
packet.remove();
listeningWhitelist.getTypes().remove(packetType);
}
}
@@ -0,0 +1,59 @@
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,12 +1,9 @@
package com.comphenix.protocol.injector.netty;
import java.net.SocketAddress;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.events.NetworkMarker;
import org.bukkit.entity.Player;
/**
* Represents an injected client connection.
@@ -15,8 +12,6 @@ import com.comphenix.protocol.events.NetworkMarker;
*/
public interface Injector {
SocketAddress getAddress();
/**
* Retrieve the current protocol version of the player.
*
@@ -28,8 +23,12 @@ 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.
*/
void inject();
boolean inject();
void uninject();
/**
* Close the current injector.
@@ -43,13 +42,20 @@ public interface Injector {
* @param marker - the network marker.
* @param filtered - whether or not the packet is filtered.
*/
void sendClientboundPacket(Object packet, NetworkMarker marker, boolean filtered);
void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered);
void readServerboundPacket(Object packet);
void receiveClientPacket(Object packet);
void sendWirePacket(WirePacket packet);
void disconnect(String message);
/**
* 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);
}
/**
* Retrieve the current protocol state. Note that since 1.20.2 the client and server direction can be in different
@@ -60,6 +66,22 @@ 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.
*
@@ -74,7 +96,7 @@ public interface Injector {
*/
void setPlayer(Player player);
boolean isConnected();
void disconnect(String message);
/**
* Determine if the channel has already been injected.
@@ -1,14 +1,10 @@
package com.comphenix.protocol.injector.netty.channel;
import java.net.SocketAddress;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.injector.netty.Injector;
import com.comphenix.protocol.injector.netty.WirePacket;
import org.bukkit.entity.Player;
final class EmptyInjector implements Injector {
@@ -20,21 +16,18 @@ 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 void inject() {
public boolean inject() {
return false;
}
@Override
public void uninject() {
}
@Override
@@ -42,19 +35,11 @@ final class EmptyInjector implements Injector {
}
@Override
public void sendClientboundPacket(Object packet, NetworkMarker marker, boolean filtered) {
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) {
}
@Override
public void readServerboundPacket(Object packet) {
}
@Override
public void sendWirePacket(WirePacket packet) {
}
@Override
public void disconnect(String message) {
public void receiveClientPacket(Object packet) {
}
@Override
@@ -62,6 +47,15 @@ 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;
@@ -73,8 +67,7 @@ final class EmptyInjector implements Injector {
}
@Override
public boolean isConnected() {
return false;
public void disconnect(String message) {
}
@Override
@@ -2,8 +2,7 @@ package com.comphenix.protocol.injector.netty.channel;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.netty.ChannelListener;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.utility.MinecraftReflection;
@@ -13,38 +12,39 @@ import io.netty.channel.ChannelInboundHandlerAdapter;
final class InboundPacketInterceptor extends ChannelInboundHandlerAdapter {
private final NettyChannelInjector injector;
private final ChannelListener channelListener;
public InboundPacketInterceptor(NettyChannelInjector injector) {
public InboundPacketInterceptor(NettyChannelInjector injector, ChannelListener listener) {
this.injector = injector;
this.channelListener = listener;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
if (MinecraftReflection.isPacketClass(msg)) {
// try get packet type
PacketType.Protocol protocol = this.injector.getInboundProtocol();
if (protocol == Protocol.UNKNOWN) {
ProtocolLogger.debug("skipping unknown inbound protocol for {0}", msg.getClass());
ctx.fireChannelRead(msg);
return;
}
// 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);
PacketType.Protocol protocol = this.injector.getInboundProtocol();
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 send the
// check if there are any listeners bound for the packet - if not just post the
// packet down the pipeline
if (!this.injector.hasInboundListener(packetType)) {
if (!this.channelListener.hasInboundListener(packetType)) {
ctx.fireChannelRead(msg);
return;
}
// don't invoke next handler and transfer packet to injector for further processing
this.injector.processInbound(ctx, new PacketContainer(packetType, msg));
// call all inbound listeners
this.injector.processInboundPacket(ctx, msg, packetType);
} else {
// just pass the message down the pipeline
ctx.fireChannelRead(msg);
@@ -14,25 +14,21 @@
*/
package com.comphenix.protocol.injector.netty.channel;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentMap;
import javax.annotation.Nonnull;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.injector.ListenerManager;
import com.comphenix.protocol.injector.netty.ChannelListener;
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 io.netty.channel.ChannelHandler;
import java.util.concurrent.ConcurrentMap;
import javax.annotation.Nonnull;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
/**
* Represents an injector factory.
@@ -43,23 +39,24 @@ import io.netty.channel.ChannelHandler;
*/
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, ErrorReporter errorReporter, ListenerManager listenerManager) {
public InjectionFactory(Plugin plugin, Server server, ErrorReporter errorReporter) {
this.plugin = plugin;
this.server = server;
this.errorReporter = errorReporter;
this.listenerManager = listenerManager;
}
/**
@@ -75,10 +72,11 @@ 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) {
public Injector fromPlayer(Player player, ChannelListener listener) {
if (this.closed) {
return new EmptyInjector(player);
}
@@ -116,9 +114,10 @@ public class InjectionFactory {
// construct a new injector as it seems like we have none yet
injector = new NettyChannelInjector(
player,
this.server,
networkManager,
channel,
this.listenerManager,
listener,
this,
this.errorReporter);
this.cacheInjector(player, injector);
@@ -156,27 +155,31 @@ 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) {
public Injector fromChannel(Channel channel, ChannelListener listener, TemporaryPlayerFactory playerFactory) {
if (this.closed) {
return EmptyInjector.WITHOUT_PLAYER;
}
Object networkManager = this.findNetworkManager(channel);
Player temporaryPlayer = TemporaryPlayerFactory.createTemporaryPlayer();
Object netManager = this.findNetworkManager(channel);
Player temporaryPlayer = playerFactory.createTemporaryPlayer(this.server);
NettyChannelInjector injector = new NettyChannelInjector(
temporaryPlayer,
networkManager,
this.server,
netManager,
channel,
this.listenerManager,
listener,
this,
this.errorReporter);
MinimalInjector minimalInjector = new NettyChannelMinimalInjector(injector);
// Initialize temporary player
TemporaryPlayerFactory.setInjectorForPlayer(temporaryPlayer, injector);
TemporaryPlayerFactory.setInjectorInPlayer(temporaryPlayer, minimalInjector);
return injector;
}
@@ -232,9 +235,9 @@ public class InjectionFactory {
* @return The associated injector, or NULL if this is a Bukkit player.
*/
private NettyChannelInjector getTemporaryInjector(Player player) {
Injector injector = TemporaryPlayerFactory.getInjectorFromPlayer(player);
if (injector instanceof NettyChannelInjector) {
return (NettyChannelInjector) injector;
MinimalInjector injector = TemporaryPlayerFactory.getInjectorFromPlayer(player);
if (injector instanceof NettyChannelMinimalInjector) {
return ((NettyChannelMinimalInjector) injector).getInjector();
}
return null;
@@ -247,12 +250,11 @@ public class InjectionFactory {
* @return The network manager.
*/
private Object findNetworkManager(Channel channel) {
Class<?> networkManagerClass = MinecraftReflection.getNetworkManagerClass();
for (Entry<String, ChannelHandler> entry : channel.pipeline()) {
if (networkManagerClass.isAssignableFrom(entry.getValue().getClass())) {
return entry.getValue();
}
// Find the network manager
Object networkManager = NettyChannelInjector.findChannelHandler(channel,
MinecraftReflection.getNetworkManagerClass());
if (networkManager != null) {
return networkManager;
}
throw new IllegalArgumentException("Unable to find NetworkManager in " + channel);
@@ -2,17 +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 org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
@@ -24,67 +21,86 @@ 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.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.entity.Player;
public class NettyChannelInjector implements Injector {
private static final String INBOUND_INTERCEPTOR_NAME = "protocol_lib_inbound_interceptor";
private static final String INBOUND_PROTOCOL_GETTER_NAME = "protocol_lib_inbound_protocol_getter";
// 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 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[] NETTY_HANDLER_NAMES = new String[]{
WIRE_PACKET_ENCODER_NAME, INBOUND_INTERCEPTOR_NAME, INBOUND_PROTOCOL_GETTER_NAME
private static final String[] PROTOCOL_LIB_HANDLERS = new String[]{
WIRE_PACKET_ENCODER_NAME, INTERCEPTOR_NAME, PROTOCOL_READER_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);
// use random attribute name because they need to be unique and would throw on reload
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();
private static final AttributeKey<Integer> PROTOCOL_VERSION = AttributeKey.valueOf(getRandomKey());
private static final AttributeKey<NettyChannelInjector> INJECTOR = AttributeKey.valueOf(getRandomKey());
static NettyChannelInjector findInjector(Channel channel) {
return channel.attr(INJECTOR).get();
}
// 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;
private static String getRandomKey() {
return "ProtocolLib-" + ThreadLocalRandom.current().nextLong();
}
// bukkit stuff
private final Server server;
// 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 channel;
private final Channel wrappedChannel;
private final ChannelListener channelListener;
private final InjectionFactory injectionFactory;
private final FieldAccessor channelField;
@@ -94,44 +110,47 @@ public class NettyChannelInjector implements Injector {
protected final ThreadLocal<Boolean> processedPackets = ThreadLocal.withInitial(() -> Boolean.FALSE);
// status of this injector
private final AtomicBoolean closed = new AtomicBoolean(false);
private volatile boolean closed = false;
private volatile boolean injected = false;
// information about the player belonging to this injector
private String playerName;
private Player player;
private Player resolvedPlayer;
// lazy initialized fields, if we don't need them we don't bother about them
private volatile InboundProtocolReader inboundProtocolReader;
private Object playerConnection;
private InboundProtocolReader inboundProtocolReader;
public NettyChannelInjector(
Player player,
Object networkManager,
Server server,
Object netManager,
Channel channel,
ListenerManager listenerManager,
ChannelListener listener,
InjectionFactory injector,
ErrorReporter errorReporter
) {
// bukkit stuff
this.player = player;
this.server = server;
this.resolvedPlayer = player;
// protocol lib stuff
this.errorReporter = errorReporter;
this.networkProcessor = new NetworkProcessor(errorReporter);
this.listenerInvoker = new PacketListenerInvoker(networkManager);
// references
this.networkManager = networkManager;
this.channel = channel;
this.listenerManager = listenerManager;
this.networkManager = netManager;
this.wrappedChannel = channel;
this.channelListener = listener;
this.injectionFactory = injector;
// register us into the channel
this.channel.attr(INJECTOR).set(this);
this.wrappedChannel.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(networkManager, true).getField(FuzzyFieldContract.newBuilder()
Field channelField = FuzzyReflection.fromObject(netManager, true).getField(FuzzyFieldContract.newBuilder()
.typeExact(Channel.class)
.banModifier(Modifier.STATIC)
.build());
@@ -140,110 +159,135 @@ 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.channel.closeFuture().addListener(future -> this.close());
this.wrappedChannel.closeFuture().addListener(future -> this.close());
}
@Override
public SocketAddress getAddress() {
return this.channel.remoteAddress();
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 int getProtocolVersion() {
Integer protocolVersion = this.channel.attr(PROTOCOL_VERSION).get();
Integer protocolVersion = this.wrappedChannel.attr(PROTOCOL_VERSION).get();
return protocolVersion == null ? MinecraftProtocolVersion.getCurrentVersion() : protocolVersion;
}
@Override
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;
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;
}
// 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;
}
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;
}
@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;
// replace wrapped channel in NetworkManager with original channel
this.channelField.set(this.networkManager, this.channel);
// remove known references to us
this.wrappedChannel.attr(INJECTOR).remove();
this.channelField.set(this.networkManager, this.wrappedChannel);
// remove all ProtocolLib netty handler
ChannelPipeline pipeline = this.channel.pipeline();
for (String handlerName : NETTY_HANDLER_NAMES) {
if (pipeline.context(handlerName) != null) {
pipeline.remove(handlerName);
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);
}
}
// mark injector as uninjected
this.injected = false;
}
@Override
public void close() {
// ensure that the injector wasn't close before
if (this.closed.compareAndSet(false, true)) {
if (!this.closed) {
this.closed = 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();
@@ -254,9 +298,9 @@ public class NettyChannelInjector implements Injector {
}
@Override
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) {
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) {
return;
}
@@ -266,12 +310,19 @@ public class NettyChannelInjector implements Injector {
}
// save the given packet marker and send the packet
if (marker != null) {
this.savedMarkers.put(packet, marker);
}
this.saveMarker(packet, marker);
try {
this.listenerInvoker.send(packet);
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);
}
}
} catch (Exception exception) {
this.errorReporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_SEND_PACKET)
.messageParam(packet, this.playerName)
@@ -281,99 +332,89 @@ public class NettyChannelInjector implements Injector {
}
@Override
public void readServerboundPacket(Object packet) {
// ignore call if the injector is closed or not injected
if (this.closed.get() || !this.injected) {
public void receiveClientPacket(Object packet) {
// do not do that if we're not injected or this injector was closed
if (this.closed || !this.injected) {
return;
}
this.ensureInEventLoop(() -> {
Runnable receiveAction = () -> {
try {
// try to invoke the method, this should normally not fail
this.listenerInvoker.read(packet);
MinecraftMethods.getNetworkManagerReadPacketMethod().invoke(this.networkManager, null, packet);
} catch (Exception exception) {
this.errorReporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_READ_PACKET)
.messageParam(packet, this.playerName)
.error(exception)
.build());
// 99% the user gave wrong information to the server
this.errorReporter.reportMinimal(this.injectionFactory.getPlugin(), "receiveClientPacket", exception);
}
});
};
// 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);
}
}
@Override
public void sendWirePacket(WirePacket packet) {
// ignore call if the injector is closed or not injected
if (this.closed.get() || !this.injected) {
return;
}
this.ensureInEventLoop(() -> {
try {
this.channel.writeAndFlush(packet);
} catch (Exception exception) {
this.errorReporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_SEND_WRITE_PACKET)
.messageParam(packet, this.playerName)
.error(exception)
.build());
}
});
}
@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());
public PacketType.Protocol getInboundProtocol() {
if (this.inboundProtocolReader != null) {
return this.inboundProtocolReader.getProtocol();
}
return getCurrentProtocol(PacketType.Sender.CLIENT);
}
@Override
public Protocol getCurrentProtocol(PacketType.Sender sender) {
return ChannelProtocolUtil.PROTOCOL_RESOLVER.apply(this.channel, 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);
}
}
@Override
public Player getPlayer() {
// if the player was already resolved there is no need to do further lookups
if (this.player != null) {
return this.player;
if (this.resolvedPlayer != null) {
return this.resolvedPlayer;
}
// check if the name of the player is already known to the injector
if (this.playerName != null) {
this.player = Bukkit.getPlayerExact(this.playerName);
if (this.player != null) {
this.injectionFactory.cacheInjector(this.player, this);
}
this.resolvedPlayer = this.server.getPlayerExact(this.playerName);
}
// either we resolved it or we didn't...
return this.player;
return this.resolvedPlayer;
}
@Override
public void setPlayer(Player player) {
this.injectionFactory.invalidate(this.player, this.playerName);
this.player = player;
this.resolvedPlayer = player;
this.playerName = player.getName();
this.injectionFactory.cacheInjector(player, this);
this.injectionFactory.cacheInjector(player.getName(), this);
}
@Override
public boolean isConnected() {
return this.channel.isActive();
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);
}
}
}
@Override
@@ -383,97 +424,139 @@ public class NettyChannelInjector implements Injector {
@Override
public boolean isClosed() {
return this.closed.get();
return this.closed;
}
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;
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())) {
if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
// 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;
}
// 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);
}
if (username != null) {
// get the username from the packet
String username = (String) LOGIN_PROFILE_ACCESSOR.get(packet);
// cache the injector and the player name
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);
}
}
// invoke plugin listeners
if (this.listenerManager.hasInboundListener(packet.getType())) {
this.processInboundInternal(ctx, packet);
} else {
ctx.fireChannelRead(packet.getHandle());
}
}
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;
}
// create event and invoke listeners
PacketEvent event = PacketEvent.fromClient(this, packetContainer, this.player);
this.listenerManager.invokeInboundPacketListeners(event);
// 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;
}
}
// get packet of event
Object packet = event.getPacket().getHandle();
// 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);
}
}
}
// fire the intercepted packet down the pipeline if it wasn't cancelled and isn't null
if (!event.isCancelled() && packet != null) {
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();
} else {
eventLoop.execute(runnable);
}
}
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));
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) {
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(event);
NetworkMarker marker = NetworkMarker.getNetworkMarker(interceptionResult);
if (marker != null) {
this.networkProcessor.invokePostEvent(event, marker);
this.networkProcessor.invokePostEvent(interceptionResult, marker);
}
}
}
<T> T processOutbound(T action) {
// try to get packet field accessor or return on failure
// 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
FieldAccessor packetAccessor = this.lookupPacketAccessor(action);
if (packetAccessor == FieldAccessor.NO_OP_ACCESSOR) {
if (packetAccessor == NO_OP_ACCESSOR) {
return action;
}
@@ -500,7 +583,7 @@ public class NettyChannelInjector implements Injector {
PacketType.Protocol protocol = this.getCurrentProtocol(PacketType.Sender.SERVER);
if (protocol == Protocol.UNKNOWN) {
ProtocolLogger.debug("skipping unknown outbound protocol for {0}", packet.getClass());
ProtocolLogger.debug("skipping unknown protocol for {0}", packet.getClass());
return action;
}
@@ -511,28 +594,30 @@ public class NettyChannelInjector implements Injector {
}
// no listener and no marker - no magic :)
if (!this.listenerManager.hasOutboundListener(packetType) && marker == null && !MinecraftReflection.isBundlePacket(packet.getClass())) {
if (!this.channelListener.hasOutboundListener(packetType) && marker == null && !MinecraftReflection.isBundlePacket(packet.getClass())) {
return action;
}
// ensure that we are on the main thread if we need to
if (this.listenerManager.hasMainThreadListener(packetType) && !Bukkit.isPrimaryThread()) {
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.sendClientboundPacket(packet, null, true));
ProtocolLibrary.getScheduler().runTask(
() -> this.sendServerPacket(packet, null, true));
return null;
}
// create event and invoke listeners
// 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
PacketContainer packetContainer = new PacketContainer(packetType, packet);
PacketEvent event = PacketEvent.fromServer(this, packetContainer, marker, this.player);
this.listenerManager.invokeOutboundPacketListeners(event);
// get packet of event
Object interceptedPacket = event.getPacket().getHandle();
PacketEvent event = this.channelListener.onPacketSending(this, packetContainer, marker);
if (event == null) {
return action;
}
// if the event wasn't cancelled by this action we must recheck if the packet changed during the method call
if (!event.isCancelled() && interceptedPacket != null) {
if (!event.isCancelled()) {
// 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);
}
@@ -582,29 +667,37 @@ public class NettyChannelInjector implements Injector {
}
private FieldAccessor lookupPacketAccessor(Object action) {
return PACKET_ACCESSORS.computeIfAbsent(action.getClass(), key -> {
return PACKET_ACCESSORS.computeIfAbsent(action.getClass(), clazz -> {
try {
Field packetField = FuzzyReflection.fromClass(key, true).getField(
FuzzyFieldContract.newBuilder()
// find the field
Field packetField = FuzzyReflection.fromClass(clazz, true).getField(FuzzyFieldContract
.newBuilder()
.typeSuperOf(MinecraftReflection.getPacketClass())
.build());
return Accessors.getFieldAccessor(packetField);
} catch (IllegalArgumentException exception) {
// return noop accessor because computeIfAbsent interprets null as a missing key
return FieldAccessor.NO_OP_ACCESSOR;
// no such field found :(
return NO_OP_ACCESSOR;
}
});
}
private void ensureInEventLoop(Runnable runnable) {
this.ensureInEventLoop(this.channel.eventLoop(), runnable);
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(EventLoop eventLoop, Runnable runnable) {
if (eventLoop.inEventLoop()) {
runnable.run();
} else {
eventLoop.execute(runnable);
}
public Channel getWrappedChannel() {
return this.wrappedChannel;
}
}
@@ -0,0 +1,44 @@
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;
}
}
@@ -1,15 +1,5 @@
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;
@@ -20,12 +10,21 @@ 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.
*/
final class NettyEventLoopProxy implements EventLoop {
abstract class NettyEventLoopProxy implements EventLoop {
private static final Callable<?> EMPTY_CALLABLE = () -> null;
private static final Runnable EMPTY_RUNNABLE = () -> {
@@ -41,7 +40,7 @@ final class NettyEventLoopProxy implements EventLoop {
private Runnable proxyRunnable(Runnable original) {
// execute the proxy and check if we need to do anything
Runnable proxied = this.injector.processOutbound(original);
Runnable proxied = this.doProxyRunnable(original);
if (proxied != null && proxied == original) {
// was not changed, we need to mark the packet as processed manually
return () -> {
@@ -56,7 +55,7 @@ final 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.injector.processOutbound(original);
Callable<T> proxied = this.doProxyCallable(original);
if (proxied != null && proxied == original) {
// was not changed, we need to mark the packet as processed manually
return () -> {
@@ -69,6 +68,26 @@ final 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();
@@ -1,237 +0,0 @@
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;
import io.netty.channel.ChannelHandlerContext;
/**
* This class facilitates the invocation of methods on the current packet listener.
* It attempts to execute the <code>send</code>, <code>read</code>, and <code>disconnect</code>
* methods and, upon failure (either due to the absence of the method or the packet
* listener being of an incorrect type), it delegates the call to the network manager.
*
* <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_READ = getNetworkManagerRead();
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 getNetworkManagerRead() {
FuzzyReflection networkManager = FuzzyReflection.fromClass(MinecraftReflection.getNetworkManagerClass(), true);
Method read = networkManager
.getMethodByParameters("read", ChannelHandlerContext.class, MinecraftReflection.getPacketClass());
return Accessors.getMethodAccessor(read);
}
private static MethodAccessor getNetworkManagerDisconnect() {
FuzzyReflection networkManager = FuzzyReflection.fromClass(MinecraftReflection.getNetworkManagerClass());
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);
}
}
/**
* Reads a packet directly using the network manager.
*
* @param packet The packet to be read.
*/
public void read(Object packet) {
NETWORK_MANAGER_READ.invoke(this.networkManager, null, packet);
}
/**
* Disconnects the player using the current packet listener if available and valid; otherwise,
* falls back to the network manager.
*
* @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);
}
}
}
@@ -1,10 +1,9 @@
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;
@@ -12,15 +11,18 @@ final class InjectionChannelInboundHandler extends ChannelInboundHandlerAdapter
private static final ReportType CANNOT_INJECT_CHANNEL = new ReportType("Unable to inject incoming channel %s.");
private final ErrorReporter errorReporter;
private final InjectionFactory injectionFactory;
private final InjectionFactory factory;
private final NetworkManagerInjector listener;
private final TemporaryPlayerFactory playerFactory;
public InjectionChannelInboundHandler(
ErrorReporter errorReporter,
InjectionFactory injectionFactory
InjectionFactory factory,
NetworkManagerInjector listener,
TemporaryPlayerFactory playerFactory
) {
this.errorReporter = errorReporter;
this.injectionFactory = injectionFactory;
this.factory = factory;
this.listener = listener;
this.playerFactory = playerFactory;
}
@Override
@@ -33,11 +35,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.injectionFactory.isClosed()) {
if (!this.factory.isClosed()) {
try {
this.injectionFactory.fromChannel(ctx.channel()).inject();
this.factory.fromChannel(ctx.channel(), this.listener, this.playerFactory).inject();
} catch (Exception exception) {
this.errorReporter.reportDetailed(this, Report.newBuilder(CANNOT_INJECT_CHANNEL)
this.listener.getReporter().reportDetailed(this, Report.newBuilder(CANNOT_INJECT_CHANNEL)
.messageParam(ctx.channel())
.error(exception)
.build());
@@ -50,7 +52,7 @@ final class InjectionChannelInboundHandler extends ChannelInboundHandlerAdapter
@Override
public boolean isSharable() {
// we do it this way to prevent the lookup overhead
// we do it this way to prevent the lookup overheat
return true;
}
}
@@ -28,7 +28,7 @@ final class InjectionChannelInitializer extends ChannelInboundHandlerAdapter {
@Override
public boolean isSharable() {
// we do it this way to prevent the lookup overhead
// we do it this way to prevent the lookup overheat
return true;
}
}
@@ -7,14 +7,21 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.bukkit.entity.Player;
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.injector.ListenerManager;
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.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;
@@ -25,36 +32,105 @@ import com.comphenix.protocol.wrappers.Pair;
import io.netty.channel.ChannelFuture;
public class NetworkManagerInjector {
public class NetworkManagerInjector implements ChannelListener {
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;
// netty handler
// injectors based on this "global" injector
private final PlayerInjectionHandler playerInjectionHandler;
private final InjectionChannelInitializer pipelineInjectorHandler;
private boolean debug = false;
// status of this injector
private boolean closed = false;
private boolean injected = false;
public NetworkManagerInjector(Plugin plugin, ListenerManager listenerManager, ErrorReporter reporter) {
public NetworkManagerInjector(Plugin plugin, Server server, ListenerInvoker listenerInvoker, ErrorReporter reporter) {
this.errorReporter = reporter;
this.injectionFactory = new InjectionFactory(plugin, reporter, listenerManager);
this.listenerInvoker = listenerInvoker;
this.injectionFactory = new InjectionFactory(plugin, server, reporter);
// hooking netty handlers
InjectionChannelInboundHandler injectionHandler = new InjectionChannelInboundHandler(
this.errorReporter,
this.injectionFactory);
this.injectionFactory,
this,
PLAYER_FACTORY);
this.pipelineInjectorHandler = new InjectionChannelInitializer(INBOUND_INJECT_HANDLER_NAME, injectionHandler);
// other injectors
this.playerInjectionHandler = new NetworkManagerPlayerInjector(
this,
this.injectionFactory);
}
public Injector getInjector(Player player) {
return this.injectionFactory.fromPlayer(player);
@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;
}
@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;
}
@SuppressWarnings("unchecked")
@@ -155,4 +231,8 @@ public class NetworkManagerInjector {
this.overriddenLists.clear();
this.injectionFactory.close();
}
public PlayerInjectionHandler getPlayerInjectionHandler() {
return this.playerInjectionHandler;
}
}
@@ -0,0 +1,68 @@
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,6 +19,8 @@ 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;
@@ -29,15 +31,18 @@ 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;
@@ -65,7 +70,7 @@ public class PacketRegistry {
*
* @author Kristian
*/
public static class Register {
private static class Register {
// The main lookup table
final Map<PacketType, Optional<Class<?>>> typeToClass = new ConcurrentHashMap<>();
@@ -77,18 +82,14 @@ public class PacketRegistry {
volatile Set<PacketType> clientPackets = new HashSet<>();
final List<MapContainer> containers = new ArrayList<>();
private Register() {
public Register() {
}
public void registerPacket(PacketType type, Class<?> packetClass, Sender sender, WrappedStreamCodec codec) {
typeToClass.put(type, Optional.of(packetClass));
classToType.put(packetClass, type);
public void registerPacket(PacketType type, Class<?> clazz, Sender sender) {
typeToClass.put(type, Optional.of(clazz));
if (codec != null) {
classToCodec.put(packetClass, codec);
}
protocolClassToType.computeIfAbsent(type.getProtocol(), __ -> new ConcurrentHashMap<>()).put(packetClass, type);
classToType.put(clazz, type);
protocolClassToType.computeIfAbsent(type.getProtocol(), __ -> new ConcurrentHashMap<>()).put(clazz, type);
if (sender == Sender.CLIENT) {
clientPackets.add(type);
@@ -292,9 +293,206 @@ 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();
ProtocolRegistry_1_20_5.fillRegister(result);
// 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);
}
}
return result;
}
@@ -319,7 +517,7 @@ public class PacketRegistry {
PacketType type = PacketType.fromCurrent(protocol, sender, packetId, packetClass);
try {
register.registerPacket(type, packetClass, sender, null);
register.registerPacket(type, packetClass, sender);
} catch (Exception ex) {
ProtocolLogger.debug("Encountered an exception associating packet " + type, ex);
}
@@ -1,71 +0,0 @@
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);
}
}
}
@@ -1,44 +0,0 @@
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);
}
});
}
}
@@ -1,102 +0,0 @@
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));
}
}
@@ -1,150 +0,0 @@
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) {
}
}
@@ -0,0 +1,81 @@
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;
}
}
@@ -0,0 +1,50 @@
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,9 +1,5 @@
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>
@@ -12,17 +8,15 @@ import com.comphenix.protocol.injector.netty.Injector;
*/
public class TemporaryPlayer {
protected volatile Injector injector;
private volatile MinimalInjector injector;
public Injector getInjector() {
MinimalInjector getInjector() {
return this.injector;
}
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");
void setInjector(MinimalInjector injector) {
if (injector == null) {
throw new IllegalArgumentException("Injector cannot be NULL.");
}
this.injector = injector;
@@ -17,20 +17,17 @@
package com.comphenix.protocol.injector.temporary;
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 java.lang.reflect.Constructor;
import java.lang.reflect.Method;
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;
@@ -39,6 +36,8 @@ 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.
@@ -47,15 +46,13 @@ 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 Injector getInjectorFromPlayer(Player player) {
public static MinimalInjector getInjectorFromPlayer(Player player) {
if (player instanceof TemporaryPlayer) {
return ((TemporaryPlayer) player).getInjector();
}
@@ -68,7 +65,7 @@ public class TemporaryPlayerFactory {
* @param player - the player to update.
* @param injector - the injector to store.
*/
public static void setInjectorForPlayer(Player player, Injector injector) {
public static void setInjectorInPlayer(Player player, MinimalInjector injector) {
((TemporaryPlayer) player).setInjector(injector);
}
@@ -79,10 +76,11 @@ public class TemporaryPlayerFactory {
public Object delegate(
@This Object obj,
@Origin Method method,
@FieldValue("injector") Injector injector,
@FieldValue("server") Server server,
@AllArguments Object... args
) throws Throwable {
String methodName = method.getName();
MinimalInjector injector = ((TemporaryPlayer) obj).getInjector();
if (injector == null) {
throw new IllegalStateException("Unable to find injector.");
@@ -94,7 +92,7 @@ public class TemporaryPlayerFactory {
} else if (methodName.equals("getAddress")) {
return injector.getAddress();
} else if (methodName.equals("getServer")) {
return Bukkit.getServer();
return server;
}
// Handle send message methods
@@ -146,15 +144,21 @@ public class TemporaryPlayerFactory {
try {
final Constructor<?> constructor = ByteBuddyFactory.getInstance()
.createSubclass(TemporaryPlayer.class, ConstructorStrategy.Default.DEFAULT_CONSTRUCTOR)
.createSubclass(TemporaryPlayer.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
.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();
.getDeclaredConstructor(Server.class);
return (Constructor<? extends Player>) constructor;
} catch (NoSuchMethodException e) {
throw new RuntimeException("Failed to find Temporary Player constructor!", e);
@@ -168,9 +172,9 @@ public class TemporaryPlayerFactory {
* @param message - a message.
* @return Always NULL.
*/
private static Object sendMessage(Injector injector, String message) {
private static Object sendMessage(MinimalInjector injector, String message) {
for (PacketContainer packet : ChatExtensions.createChatPackets(message)) {
injector.sendClientboundPacket(packet.getHandle(), null, false);
injector.sendServerPacket(packet.getHandle(), null, false);
}
return null;
@@ -193,13 +197,27 @@ 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 static Player createTemporaryPlayer() {
public Player createTemporaryPlayer(final Server server) {
try {
return PLAYER_CONSTRUCTOR.newInstance();
return PLAYER_CONSTRUCTOR.newInstance(server);
} 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,21 +4,6 @@ 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,25 +9,6 @@ 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,18 +1,16 @@
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 {
@@ -60,20 +58,14 @@ public class MinecraftGenerator {
try {
String name = type.getName();
if (name.contains("it.unimi.dsi.fastutil")) {
// convert interface maps to OpenHashMaps
if (!name.endsWith("OpenHashMap")) {
name = name.replace("Map", "OpenHashMap");
}
Class<?> hashMapClass = Class.forName(name);
return Accessors.getConstructorAccessorOrNull(hashMapClass);
Class<?> clz = Class.forName(name.replace("Map", "OpenHashMap"));
return Accessors.getConstructorAccessorOrNull(clz);
}
} catch (Exception ignored) {
}
return ConstructorAccessor.NO_OP_ACCESSOR;
return null;
});
if (ctor != ConstructorAccessor.NO_OP_ACCESSOR) {
if (ctor != null) {
try {
return ctor.invoke();
} catch (Exception ignored) {
@@ -35,13 +35,12 @@ import net.bytebuddy.matcher.ElementMatchers;
public final class MinecraftMethods {
// For player connection
private volatile static MethodAccessor playerConnectionSendMethod;
private volatile static MethodAccessor playerConnectionDisconnectMethod;
private volatile static MethodAccessor sendPacketMethod;
private volatile static MethodAccessor disconnectMethod;
// For network manager
private volatile static MethodAccessor networkManagerSend;
private volatile static MethodAccessor networkManagerHandle;
private volatile static MethodAccessor networkManagerPacketRead;
private volatile static MethodAccessor networkManagerDisconnect;
// For packet
private volatile static MethodAccessor packetReadByteBuf;
@@ -79,18 +78,18 @@ public final class MinecraftMethods {
*
* @return The send packet method.
*/
public static MethodAccessor getPlayerConnectionSendMethod() {
if (playerConnectionSendMethod == null) {
public static MethodAccessor getSendPacketMethod() {
if (sendPacketMethod == null) {
FuzzyReflection serverHandlerClass = FuzzyReflection.fromClass(MinecraftReflection.getPlayerConnectionClass());
try {
playerConnectionSendMethod = Accessors.getMethodAccessor(serverHandlerClass.getMethod(FuzzyMethodContract.newBuilder()
sendPacketMethod = Accessors.getMethodAccessor(serverHandlerClass.getMethod(FuzzyMethodContract.newBuilder()
.parameterCount(1)
.returnTypeVoid()
.parameterExactType(MinecraftReflection.getPacketClass(), 0)
.build()));
} catch (IllegalArgumentException e) {
playerConnectionSendMethod = Accessors.getMethodAccessor(serverHandlerClass.getMethod(FuzzyMethodContract.newBuilder()
sendPacketMethod = Accessors.getMethodAccessor(serverHandlerClass.getMethod(FuzzyMethodContract.newBuilder()
.nameRegex("sendPacket.*")
.returnTypeVoid()
.parameterCount(1)
@@ -98,41 +97,33 @@ public final class MinecraftMethods {
}
}
return playerConnectionSendMethod;
return sendPacketMethod;
}
/**
* Retrieve the disconnect method for a given player connection.
*
* @param playerConnection - the player connection.
* @return The
*/
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()
public static MethodAccessor getDisconnectMethod(Class<?> playerConnection) {
if (disconnectMethod == null) {
FuzzyReflection playerConnectionClass = FuzzyReflection.fromClass(playerConnection);
try {
disconnectMethod = Accessors.getMethodAccessor(playerConnectionClass.getMethod(FuzzyMethodContract.newBuilder()
.returnTypeVoid()
.nameRegex("disconnect.*")
.parameterCount(1)
.parameterExactType(MinecraftReflection.getIChatBaseComponentClass(), 0)
.parameterExactType(String.class, 0)
.build()));
} 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);
}
} catch (IllegalArgumentException e) {
// Just assume it's the first String method
Method disconnect = playerConnectionClass.getMethodByParameters("disconnect", String.class);
disconnectMethod = Accessors.getMethodAccessor(disconnect);
}
}
return playerConnectionDisconnectMethod;
return disconnectMethod;
}
/**
@@ -140,8 +131,8 @@ public final class MinecraftMethods {
*
* @return The handle method.
*/
public static MethodAccessor getNetworkManagerSendMethod() {
if (networkManagerSend == null) {
public static MethodAccessor getNetworkManagerHandleMethod() {
if (networkManagerHandle == null) {
Method handleMethod = FuzzyReflection
.fromClass(MinecraftReflection.getNetworkManagerClass(), true)
.getMethod(FuzzyMethodContract.newBuilder()
@@ -150,10 +141,10 @@ public final class MinecraftMethods {
.parameterCount(1)
.parameterExactType(MinecraftReflection.getPacketClass(), 0)
.build());
networkManagerSend = Accessors.getMethodAccessor(handleMethod);
networkManagerHandle = Accessors.getMethodAccessor(handleMethod);
}
return networkManagerSend;
return networkManagerHandle;
}
/**
@@ -172,27 +163,6 @@ 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,9 +93,6 @@ 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,11 +373,6 @@ 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());
}
@@ -507,10 +502,6 @@ public final class MinecraftReflection {
return is(getCraftItemStackClass(), obj);
}
public static boolean isIChatBaseComponent(Class<?> target) {
return is(getIChatBaseComponentClass(), target);
}
/**
* Retrieve the EntityPlayer (NMS) class.
*
@@ -685,8 +676,7 @@ 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", "network.chat.ComponentSerialization", "IChatBaseComponent$ChatSerializer");
return getMinecraftClass("network.chat.IChatBaseComponent$ChatSerializer", "network.chat.Component$Serializer", "IChatBaseComponent$ChatSerializer");
}
/**
@@ -36,26 +36,6 @@ 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
*/
@@ -169,7 +149,7 @@ public final class MinecraftVersion implements Comparable<MinecraftVersion>, Ser
/**
* The latest release version of minecraft.
*/
public static final MinecraftVersion LATEST = v1_21_6;
public static final MinecraftVersion LATEST = v1_21_0;
// used when serializing
private static final long serialVersionUID = -8695133558996459770L;
@@ -10,9 +10,7 @@ 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;
@@ -33,7 +31,6 @@ 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;
@@ -238,35 +235,14 @@ public class StreamSerializer {
Object serializer = MinecraftReflection.getPacketDataSerializer(buf);
if (READ_ITEM_METHOD == null) {
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()));
}
READ_ITEM_METHOD = Accessors.getMethodAccessor(FuzzyReflection
.fromClass(serializer.getClass(), false)
.getMethodByReturnTypeAndParameters("readItemStack", MinecraftReflection.getItemStackClass()));
}
try {
// unwrap the item
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);
}
Object nmsItem = READ_ITEM_METHOD.invoke(serializer);
return nmsItem != null ? MinecraftReflection.getBukkitItemStack(nmsItem) : null;
} finally {
ReferenceCountUtil.safeRelease(buf);
@@ -286,26 +262,11 @@ 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) {
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()));
}
WRITE_ITEM_METHOD = Accessors.getMethodAccessor(FuzzyReflection
.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true)
.getMethodByParameters("writeStack", MinecraftReflection.getItemStackClass()));
}
ByteBuf buf = Unpooled.buffer();
@@ -313,12 +274,7 @@ public class StreamSerializer {
// Get the NMS version of the ItemStack and write it into the buffer
Object nmsItem = MinecraftReflection.getMinecraftItemStack(stack);
if (MinecraftVersion.v1_21_0.atOrAbove()) {
WRITE_ITEM_METHOD.invoke(STREAM_CODEC.get(null), serializer, nmsItem);
} else {
WRITE_ITEM_METHOD.invoke(serializer, nmsItem);
}
WRITE_ITEM_METHOD.invoke(serializer, nmsItem);
// write the serialized content to the stream
output.write(this.getBytesAndRelease(buf));
@@ -86,15 +86,6 @@ 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,10 +11,6 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang.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;
@@ -27,6 +23,10 @@ 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,8 +140,6 @@ public abstract class EnumWrappers {
UPDATE_LISTED,
UPDATE_LATENCY,
UPDATE_DISPLAY_NAME,
UPDATE_LIST_ORDER,
UPDATE_HAT,
/**
* @deprecated Removed in 1.19.3
*/
@@ -193,18 +191,8 @@ 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,
@@ -344,8 +332,7 @@ public abstract class EnumWrappers {
NEUTRAL("neutral"),
PLAYERS("player"),
AMBIENT("ambient"),
VOICE("voice"),
UI("ui");
VOICE("voice");
private static final Map<String, SoundCategory> LOOKUP;
static {
@@ -377,8 +364,7 @@ public abstract class EnumWrappers {
LEGS,
CHEST,
HEAD,
BODY,
SADDLE
BODY
}
public enum Hand {
@@ -637,86 +623,6 @@ 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;
@@ -741,12 +647,7 @@ 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<>();
@@ -759,143 +660,116 @@ public abstract class EnumWrappers {
if (INITIALIZED)
return;
synchronized (EnumWrappers.class) {
// Recheck initialization status inside the lock
if (INITIALIZING || INITIALIZED)
return;
INITIALIZED = true;
// Prevent circular calls to initialize during initialization
// (certain methods below indirectly call initialize again)
INITIALIZING = true;
PROTOCOL_CLASS = MinecraftReflection.getEnumProtocolClass();
CLIENT_COMMAND_CLASS = getEnum(PacketType.Play.Client.CLIENT_COMMAND.getPacketClass(), 0);
PROTOCOL_CLASS = MinecraftReflection.getEnumProtocolClass();
CLIENT_COMMAND_CLASS = getEnum(PacketType.Play.Client.CLIENT_COMMAND.getPacketClass(), 0);
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()) {
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);
}
try {
DIFFICULTY_CLASS = getEnum(PacketType.Play.Server.SERVER_DIFFICULTY.getPacketClass(), 0);
} catch (Exception ex) {
DIFFICULTY_CLASS = getEnum(PacketType.Play.Server.LOGIN.getPacketClass(), 1);
}
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()) {
GAMEMODE_CLASS = getEnum(MinecraftReflection.getPlayerInfoDataClass(), 0);
} else {
GAMEMODE_CLASS = getEnum(PacketType.Play.Server.LOGIN.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);
}
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);
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);
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];
}
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 {
SOUND_CATEGORY_CLASS = MinecraftReflection.getMinecraftClass("sounds.SoundCategory");
} catch (Exception ex) {
SOUND_CATEGORY_CLASS = getEnum(PacketType.Play.Server.NAMED_SOUND_EFFECT.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);
}
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 {
// 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);
}
// 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");
// 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");
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getEnumEntityUseActionClass(), true);
Method getType = fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterCount(0)
.returnTypeMatches(FuzzyMatchers.except(Void.class))
.build());
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getEnumEntityUseActionClass(), true);
Method getType = fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterCount(0)
.returnTypeMatches(FuzzyMatchers.except(Void.class))
.build());
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);
}
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);
}
// 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);
}
// 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);
}
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");
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");
RENDER_TYPE_CLASS = MinecraftReflection.getNullableNMS(
"world.scores.criteria.ObjectiveCriteria$RenderType",
"world.scores.criteria.IScoreboardCriteria$EnumScoreboardHealthDisplay",
"IScoreboardCriteria$EnumScoreboardHealthDisplay");
CHAT_FORMATTING_CLASS = MinecraftReflection.getNullableNMS("ChatFormatting", "EnumChatFormat");
RENDER_TYPE_CLASS = MinecraftReflection.getNullableNMS(
"world.scores.criteria.ObjectiveCriteria$RenderType",
"world.scores.criteria.IScoreboardCriteria$EnumScoreboardHealthDisplay",
"IScoreboardCriteria$EnumScoreboardHealthDisplay");
CHAT_FORMATTING_CLASS = MinecraftReflection.getNullableNMS("ChatFormatting", "EnumChatFormat");
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());
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;
if (ENTITY_POSE_CLASS != null) {
associate(ENTITY_POSE_CLASS, EntityPose.class, getEntityPoseConverter());
}
}
@@ -1059,26 +933,6 @@ 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);
@@ -1172,22 +1026,6 @@ 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)
@@ -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,7 +29,8 @@ 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 com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
import javax.annotation.Nullable;
/**
* Represents an immutable PlayerInfoData in the PLAYER_INFO packet.
@@ -40,9 +41,7 @@ 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;
@@ -75,21 +74,6 @@ 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)
@@ -112,12 +96,10 @@ 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.21.2 or later.
* 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
@@ -125,43 +107,9 @@ 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, 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) {
public PlayerInfoData(UUID profileId, int latency, boolean listed, NativeGameMode gameMode, WrappedGameProfile profile, WrappedChatComponent displayName, @Nullable WrappedRemoteChatSessionData remoteChatSession) {
this.profileId = profileId;
this.latency = latency;
this.listed = listed;
@@ -170,8 +118,6 @@ public class PlayerInfoData {
this.displayName = displayName;
this.profileKeyData = null;
this.remoteChatSessionData = remoteChatSession;
this.showHat = showHat;
this.listOrder = listOrder;
}
/**
@@ -233,22 +179,6 @@ 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.
@@ -272,9 +202,7 @@ public class PlayerInfoData {
* @return A new converter.
*/
public static EquivalentConverter<PlayerInfoData> getConverter() {
return new EquivalentConverter<>() {
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
return new EquivalentConverter<PlayerInfoData>() {
@Override
public Object getGeneric(PlayerInfoData specific) {
if (constructor == null) {
@@ -297,20 +225,13 @@ 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(EMPTY_CLASS_ARRAY));
constructor = MinecraftReflection.getPlayerInfoDataClass().getConstructor(args.toArray(new Class<?>[0]));
} catch (Exception e) {
throw new RuntimeException("Cannot find PlayerInfoData constructor.", e);
}
@@ -319,72 +240,32 @@ public class PlayerInfoData {
// Attempt to construct the underlying PlayerInfoData
try {
Object gameMode = Converters.ignoreNull(EnumWrappers.getGameModeConverter()).getGeneric(specific.gameMode);
Object gameMode = EnumWrappers.getGameModeConverter().getGeneric(specific.gameMode);
Object displayName = specific.displayName != null ? specific.displayName.handle : null;
Object profile = specific.profile != null ? specific.profile.handle : 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
};
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
);
} else if (MinecraftVersion.WILD_UPDATE.atOrAbove()) {
args = new Object[] {
profile,
specific.latency,
gameMode,
displayName,
specific.profileKeyData == null ? null : specific.profileKeyData.handle
};
return constructor.newInstance(
profile,
specific.latency,
gameMode,
displayName,
specific.profileKeyData == null ? null : specific.profileKeyData.handle);
} else if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
args = new Object[] {
profile,
specific.latency,
gameMode,
displayName
};
return constructor.newInstance(profile, specific.latency, gameMode, displayName);
} else {
args = new Object[] {
null,
profile,
specific.latency,
gameMode,
displayName
};
return constructor.newInstance(null, profile, specific.latency, gameMode, displayName);
}
return constructor.newInstance(args);
} catch (Exception e) {
throw new RuntimeException("Failed to construct PlayerInfoData.", e);
}
@@ -394,60 +275,37 @@ 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(), Converters.ignoreNull(EnumWrappers.getGameModeConverter()));
EnumWrappers.getGameModeClass(), 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.v1_21_4.atOrAbove()) {
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.<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)
);
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);
}
@@ -1,14 +1,8 @@
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.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import org.bukkit.ChatColor;
import com.comphenix.protocol.reflect.FuzzyReflection;
@@ -41,49 +35,41 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
private static ConstructorAccessor CONSTRUCT_TEXT_COMPONENT = null;
private static WrappedCodec CODEC;
static {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(SERIALIZER, true);
FuzzyReflection reflection = 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));
} else if (MinecraftVersion.v1_20_5.atOrAbove()) {
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
// Retrieve the correct methods
if (MinecraftVersion.v1_20_5.atOrAbove()) {
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(reflection.getMethod(FuzzyMethodContract.newBuilder()
.returnTypeExact(String.class)
.parameterDerivedOf(COMPONENT)
.parameterDerivedOf(MinecraftReflection.getHolderLookupProviderClass())
.build()));
} else {
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters("serialize", /* a */
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(reflection.getMethodByReturnTypeAndParameters("serialize", /* a */
String.class, new Class<?>[] { COMPONENT }));
}
if (!MinecraftVersion.v1_20_4.atOrAbove()) {
GSON = Accessors.getFieldAccessor(fuzzy.getFieldByType("gson", GSON_CLASS)).get(null);
}
GSON = Accessors.getFieldAccessor(reflection.getFieldByType("gson", GSON_CLASS)).get(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;
}
}
}
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;
}
}
// Get a component from a standard Minecraft message
CONSTRUCT_COMPONENT = Accessors.getMethodAccessor(MinecraftReflection.getCraftChatMessage(), "fromString", String.class, boolean.class);
@@ -97,11 +83,6 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
}
private static Object serialize(Object handle) {
if (CODEC != null) {
Object jobj = CODEC.encode(handle, WrappedDynamicOps.json(false)).getOrThrow(JsonParseException::new);
return jobj.toString();
}
if (MinecraftVersion.v1_20_5.atOrAbove()) {
return SERIALIZE_COMPONENT.invoke(null, handle, MinecraftRegistryAccess.get());
}
@@ -110,10 +91,6 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
}
private static Object deserialize(String json) {
if (CODEC != null) {
return CODEC.parse(JsonParser.parseString(json), WrappedDynamicOps.json(false)).getOrThrow(JsonParseException::new);
}
if (MinecraftVersion.v1_20_5.atOrAbove()) {
return DESERIALIZE.invoke(null, json, MinecraftRegistryAccess.get());
}
@@ -24,7 +24,6 @@ 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;
@@ -41,6 +40,7 @@ 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;
@@ -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,36 +911,23 @@ 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 Type type;
private final Class<?> 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(Type type, Object handle, boolean optional) {
super(HANDLE_TYPE);
this.type = type;
this.optional = optional;
@@ -950,32 +937,17 @@ 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() {
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;
return 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() {
@@ -1014,19 +986,11 @@ public class WrappedDataWatcher implements IDataWatcher {
public static class Registry {
private static boolean INITIALIZED = false;
private static Map<Type, Serializer> RAW_REGISTRY = null;
private static Map<Type, Serializer> OPTIONAL_REGISTRY = null;
private static Map<Class<?>, Serializer> RAW_REGISTRY = null;
private static Map<Class<?>, Serializer> OPTIONAL_REGISTRY = null;
/**
* @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.
* Gets the first serializer associated with a given class.
*
* <p><b>Note</b>: If {@link Serializer#isOptional() the serializer is optional},
* values <i>must</i> be wrapped in an {@link Optional}.</p>
@@ -1034,46 +998,39 @@ 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 type Type to find serializer for
* @param clazz Class to find serializer for
* @return The serializer, or null if none exists
*/
public static Serializer get(Type type) {
Validate.notNull(type,"Type cannot be null!");
public static Serializer get(Class<?> clazz) {
Validate.notNull(clazz,"Class cannot be null!");
initialize();
Serializer serializer = RAW_REGISTRY.getOrDefault(type, OPTIONAL_REGISTRY.get(type));
Serializer serializer = RAW_REGISTRY.getOrDefault(clazz,
OPTIONAL_REGISTRY.getOrDefault(clazz, null));
if (serializer == null) {
throw new IllegalArgumentException("No serializer found for " + type);
throw new IllegalArgumentException("No serializer found for " + clazz);
}
return serializer;
}
/**
* @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.
* Gets the first serializer associated with a given class and optional state.
* <br/>
* <b>Note</b>: If the serializer is optional, values <i>must</i> be
* wrapped in an {@link Optional}
*
* @param type Type to find serializer for
* @param clazz Class to find serializer for
* @param optional Optional state
* @return The serializer, or null if none exists
*/
public static Serializer get(Type type, boolean optional) {
Validate.notNull(type, "Type cannot be null!");
public static Serializer get(Class<?> clazz, boolean optional) {
Validate.notNull(clazz, "Class cannot be null!");
initialize();
Serializer serializer = optional ? OPTIONAL_REGISTRY.get(type) : RAW_REGISTRY.get(type);
Serializer serializer = optional ? OPTIONAL_REGISTRY.get(clazz) : RAW_REGISTRY.get(clazz);
if (serializer == null) {
throw new IllegalArgumentException("No serializer found for " + (optional ? "Optional<" + type + ">" : type));
throw new IllegalArgumentException("No serializer found for " + (optional ? "Optional<" + clazz + ">" : clazz));
}
return serializer;
@@ -1110,41 +1067,52 @@ public class WrappedDataWatcher implements IDataWatcher {
return;
}
Map<Type, Serializer> rawRegistry = new HashMap<>();
Map<Type, Serializer> optionalRegistry = new HashMap<>();
Map<Class<?>, Serializer> rawRegistry = new HashMap<>();
Map<Class<?>, Serializer> optionalRegistry = new HashMap<>();
List<Field> candidates = FuzzyReflection.fromClass(MinecraftReflection.getDataWatcherRegistryClass(), true)
.getFieldListByType(MinecraftReflection.getDataWatcherSerializerClass());
for (Field candidate : candidates) {
if (candidate.getGenericType() instanceof ParameterizedType cpt) {
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);
}
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);
}
// 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 (serializer == null) {
throw new RuntimeException("Failed to read serializer: " + candidate.getName());
}
Serializer wrappedSerializer = new Serializer(serializingType, serializer, false);
rawRegistry.put(serializingType, wrappedSerializer);
if (optional) {
optionalRegistry.put(innerClass, new Serializer(innerClass, serializer, true));
} else {
rawRegistry.put(innerClass, new Serializer(innerClass, serializer, false));
}
}
}
RAW_REGISTRY = Map.copyOf(rawRegistry);
OPTIONAL_REGISTRY = Map.copyOf(optionalRegistry);
RAW_REGISTRY = ImmutableMap.copyOf(rawRegistry);
OPTIONAL_REGISTRY = ImmutableMap.copyOf(optionalRegistry);
}
// ---- Helper methods
@@ -4,9 +4,7 @@ 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;
@@ -43,7 +41,6 @@ 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 {
@@ -57,17 +54,9 @@ public final class WrappedLevelChunkData {
BLOCK_ENTITIES_DATA_ACCESSOR = Accessors.getFieldAccessor(reflection.getField(FuzzyFieldContract.newBuilder()
.typeExact(List.class)
.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;
}
HEIGHTMAPS_ACCESSOR = Accessors.getFieldAccessor(reflection.getField(FuzzyFieldContract.newBuilder()
.typeExact(MinecraftReflection.getNBTCompoundClass())
.build()));
BUFFER_ACCESSOR = Accessors.getFieldAccessor(reflection.getField(FuzzyFieldContract.newBuilder().typeExact(byte[].class).build()));
}
@@ -79,50 +68,22 @@ 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.
*
@@ -169,16 +130,12 @@ 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));
@@ -188,24 +145,6 @@ 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,80 +183,59 @@ public class WrappedParticle<T> {
}
private static Object getRedstone(Object handle) {
Color color;
float scale;
int r, g, b;
float size;
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()) {
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
StructureModifier<Object> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle);
org.joml.Vector3f rgb = (org.joml.Vector3f) modifier.withType(org.joml.Vector3f.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);
r = (int) (rgb.x() * 255);
g = (int) (rgb.y() * 255);
b = (int) (rgb.z() * 255);
size = (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);
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);
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);
} else {
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);
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);
}
return new Particle.DustOptions(color, scale);
return new Particle.DustOptions(Color.fromRGB(r, g, b), size);
}
private static Object getDustTransition(Object handle) {
Color fromColor, toColor;
float scale;
int fromR, fromG, fromB, toR, toG, toB;
float size;
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()) {
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);
scale = (float) modifier.withType(float.class).read(0);
size = (float) modifier.withType(float.class).read(0);
int fromR = (int) (fromRGB.x() * 255);
int fromG = (int) (fromRGB.y() * 255);
int fromB = (int) (fromRGB.z() * 255);
fromColor = Color.fromRGB(fromR, fromG, fromB);
fromR = (int) (fromRGB.x() * 255);
fromG = (int) (fromRGB.y() * 255);
fromB = (int) (fromRGB.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);
toR = (int) (toRGB.x() * 255);
toG = (int) (toRGB.y() * 255);
toB = (int) (toRGB.z() * 255);
} else if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
if (VECTOR_3FA == null) {
VECTOR_3FA = MinecraftReflection.getLibraryClass("com.mojang.math.Vector3fa");
@@ -266,42 +245,37 @@ public class WrappedParticle<T> {
Object toRGB = modifier.withType(VECTOR_3FA).read(0);
Object fromRGB = modifier.withType(VECTOR_3FA).read(1);
scale = (float) modifier.withType(float.class).read(0);
size = (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);
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);
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 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);
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);
} else {
StructureModifier<Float> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle).withType(float.class);
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);
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);
}
return new Particle.DustTransition(fromColor, toColor, scale);
return new Particle.DustTransition(Color.fromRGB(fromR, fromG, fromB), Color.fromRGB(toR, toG, toB), size);
}
public static <T> WrappedParticle<T> create(Particle particle, T data) {
ensureMethods();
Object bukkitData = data;
if (data instanceof WrappedBlockData blockData) {
if (data instanceof WrappedBlockData) {
WrappedBlockData blockData = (WrappedBlockData) data;
bukkitData = toCraftData.invoke(null, blockData.getHandle());
}
@@ -1,15 +1,11 @@
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.
@@ -39,16 +35,11 @@ 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
@@ -67,32 +58,19 @@ public class WrappedTeamParameters extends AbstractWrapper {
}
@NotNull
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;
}
public String getNametagVisibility() {
return modifier.<String>withType(String.class).read(0);
}
@NotNull
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;
}
public String getCollisionRule() {
return modifier.<String>withType(String.class).read(1);
}
@NotNull
public EnumWrappers.ChatFormatting getColor() {
return modifier
.withType(EnumWrappers.getChatFormattingClass(), EnumWrappers.getChatFormattingConverter())
.read(0);
Object handle = modifier.withType(EnumWrappers.getChatFormattingClass()).read(0);
return EnumWrappers.getChatFormattingConverter().getSpecific(handle);
}
public int getOptions() {
@@ -110,16 +88,15 @@ public class WrappedTeamParameters extends AbstractWrapper {
public static class Builder {
private WrappedChatComponent displayName, prefix, suffix;
private TeamVisibility nametagVisibility;
private TeamCollisionRule collisionRule;
private String nametagVisibility, collisionRule;
private EnumWrappers.ChatFormatting color;
private int options;
private Builder(@Nullable WrappedTeamParameters template) {
if (template != null) {
this.displayName = template.getDisplayName();
this.prefix = template.getPrefix();
this.suffix = template.getSuffix();
this.prefix = template.getDisplayName();
this.suffix = template.getDisplayName();
this.nametagVisibility = template.getNametagVisibility();
this.collisionRule = template.getCollisionRule();
this.color = template.getColor();
@@ -145,36 +122,18 @@ public class WrappedTeamParameters extends AbstractWrapper {
return this;
}
public Builder nametagVisibility(@NotNull TeamVisibility nametagVisibility) {
public Builder nametagVisibility(@NotNull String nametagVisibility) {
Preconditions.checkNotNull(nametagVisibility);
this.nametagVisibility = nametagVisibility;
return this;
}
/**
* 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) {
public Builder collisionRule(@NotNull String 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;
@@ -201,15 +160,8 @@ public class WrappedTeamParameters extends AbstractWrapper {
wrapped.writeComponent(0, displayName);
wrapped.writeComponent(1, prefix);
wrapped.writeComponent(2, suffix);
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(String.class).write(0, nametagVisibility);
wrapped.modifier.withType(String.class).write(1, collisionRule);
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. For heterogeneous lists this will return {@link NbtType#TAG_COMPOUND TAG_COMPOUND}.
* Get the type of each element.
* <p>
* This will be {@link NbtType#TAG_END TAG_END} if the NBT list has just been created.
* @return Element type.
@@ -32,12 +32,8 @@ 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);
/**
@@ -45,19 +41,9 @@ 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,27 +1,29 @@
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.
@@ -32,10 +34,6 @@ 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.
@@ -47,7 +45,7 @@ class TileEntityAccessor<T extends BlockState> {
*/
private static final Map<Class<?>, TileEntityAccessor<?>> cachedAccessors = new HashMap<>();
private MethodAccessor createInputWrapper;
private static Constructor<?> nbtCompoundParserConstructor;
private FieldAccessor tileEntityField;
private MethodAccessor readCompound;
@@ -106,50 +104,11 @@ class TileEntityAccessor<T extends BlockState> {
}
void findMethods(Class<?> type, T state) {
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) {
if (BLOCK_DATA_INCL) {
Class<?> tileEntityClass = MinecraftReflection.getTileEntityClass();
Class<?> iBlockData = MinecraftReflection.getIBlockDataClass();
Class<?> nbtCompound = MinecraftReflection.getNBTCompoundClass();
FuzzyReflection fuzzy = FuzzyReflection.fromClass(tileEntityClass, false);
writeCompound = Accessors.getMethodAccessor(fuzzy.getMethod(
FuzzyMethodContract.newBuilder()
@@ -255,17 +214,12 @@ 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
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;
}
writeCompound.invoke(tileEntity, NbtFactory.fromBase(output).getHandle());
return output;
}
/**
@@ -278,14 +232,7 @@ class TileEntityAccessor<T extends BlockState> {
Object tileEntity = tileEntityField.get(state);
// Ensure the block state is set to the compound
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) {
if (BLOCK_DATA_INCL) {
Object blockData = BukkitUnwrapper.getInstance().unwrapItem(state);
readCompound.invoke(tileEntity, blockData, NbtFactory.fromBase(compound).getHandle());
} else {
@@ -17,7 +17,6 @@
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;
@@ -104,9 +103,7 @@ class WrappedList<TType> implements NbtWrapper<List<NbtBase<TType>>>, NbtList<TT
*/
public WrappedList(Object handle) {
this.container = new WrappedElement<>(handle);
if (!MinecraftVersion.v1_21_5.atOrAbove()) {
this.elementType = container.getSubType();
}
this.elementType = container.getSubType();
}
/**
@@ -116,9 +113,7 @@ class WrappedList<TType> implements NbtWrapper<List<NbtBase<TType>>>, NbtList<TT
*/
public WrappedList(Object handle, String name) {
this.container = new WrappedElement<>(handle, name);
if (!MinecraftVersion.v1_21_5.atOrAbove()) {
this.elementType = container.getSubType();
}
this.elementType = container.getSubType();
}
@Override
@@ -146,28 +141,13 @@ class WrappedList<TType> implements NbtWrapper<List<NbtBase<TType>>>, NbtList<TT
@Override
public NbtType getElementType() {
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;
}
return elementType;
}
@Override
public void setElementType(NbtType type) {
this.elementType = type;
if (!MinecraftVersion.v1_21_5.atOrAbove()) {
container.setSubType(type);
}
container.setSubType(type);
}
@Override
@@ -191,16 +171,14 @@ 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.");
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());
// 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());
}
}
@@ -256,22 +234,17 @@ class WrappedList<TType> implements NbtWrapper<List<NbtBase<TType>>>, NbtList<TT
return (NbtBase) container.deepClone();
}
@Override
public void addClosest(Object value) {
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) {
public void addClosest(Object value) {
if (getElementType() == NbtType.TAG_END)
throw new IllegalStateException("This list has not been typed yet.");
if (value instanceof Number) {
Number number = (Number) value;
// Convert the number
switch (type) {
switch (getElementType()) {
case TAG_BYTE: add(number.byteValue()); break;
case TAG_SHORT: add(number.shortValue()); break;
case TAG_INT: add(number.intValue()); break;
@@ -289,7 +262,7 @@ class WrappedList<TType> implements NbtWrapper<List<NbtBase<TType>>>, NbtList<TT
} else {
// Just add it
add((NbtBase<TType>) NbtFactory.ofWrapper(type, EMPTY_NAME, value));
add((NbtBase<TType>) NbtFactory.ofWrapper(getElementType(), EMPTY_NAME, value));
}
}
@@ -382,7 +355,7 @@ class WrappedList<TType> implements NbtWrapper<List<NbtBase<TType>>>, NbtList<TT
}
// Update the sub type as well
if (lastElement != null && !MinecraftVersion.v1_21_5.atOrAbove()) {
if (lastElement != null) {
container.setSubType(lastElement.getType());
}
}
@@ -261,9 +261,10 @@ 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), type);
list.addClosest(toNodeValue(value, type));
}
// Add the list
@@ -1,16 +1,8 @@
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;
@@ -21,6 +13,13 @@ 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,5 +1,10 @@
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;
@@ -10,13 +15,6 @@ 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,18 +1,17 @@
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,64 +1,45 @@
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;
import net.minecraft.core.HolderLookup;
import net.minecraft.commands.Commands.CommandSelection;
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_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.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.spigotmc.SpigotWorldConfig;
import static org.mockito.ArgumentMatchers.any;
@@ -100,6 +81,8 @@ public class BukkitInitialization {
synchronized (initLock) {
if (initialized) {
return;
} else {
initialized = true;
}
try {
@@ -112,42 +95,36 @@ public class BukkitInitialization {
instance.setPackage();
// Minecraft Data Init
SharedConstants.tryDetectVersion(); // .tryDetectVersion()
Bootstrap.bootStrap(); // .bootStrap()
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
PackRepository repo = ServerPacksSource.createVanillaTrustedRepository();
repo.reload();
PackRepository resourcePackRepository = ServerPacksSource.createVanillaTrustedRepository();
resourcePackRepository.reload();
ResourceManager resourceManager = new MultiPackResourceManager(
CloseableResourceManager resourceManager = new MultiPackResourceManager(
PackType.SERVER_DATA,
repo.getAvailablePacks().stream().map(Pack::open).collect(Collectors.toList())
resourcePackRepository.getAvailablePacks().stream().map(Pack::open).collect(Collectors.toList())
);
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();
LayeredRegistryAccess<RegistryLayer> layeredRegistryAccess = RegistryLayer.createRegistryAccess();
layeredRegistryAccess = WorldLoader.loadAndReplaceLayer(resourceManager, layeredRegistryAccess, RegistryLayer.WORLDGEN, RegistryDataLoader.WORLDGEN_REGISTRIES);
RegistryAccess.Frozen registryCustom = layeredRegistryAccess.compositeAccess().freeze();
ReloadableServerResources dataPackResources = ReloadableServerResources.loadResources(
resourceManager,
layeredAccess2,
tags,
FeatureFlags.REGISTRY.allFlags() /* REGISTRY.allFlags() */,
Commands.CommandSelection.DEDICATED /* DEDICATED */,
layeredRegistryAccess,
FeatureFlags.REGISTRY.allFlags(),
CommandSelection.DEDICATED,
0,
MoreExecutors.directExecutor(),
MoreExecutors.directExecutor()
).join();
dataPackResources.updateStaticRegistryTags();
dataPackResources.updateRegistryTags();
/* try {
IRegistry.class.getName();
try {
RegistryAccess.class.getName();
} catch (Throwable ex) {
ex.printStackTrace();
} */
}
String releaseTarget = MinecraftReflectionTestUtil.RELEASE_TARGET;
String serverVersion = CraftServer.class.getPackage().getImplementationVersion();
@@ -171,56 +148,15 @@ 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<Keyed> registryType = invocation.getArgument(0);
Object registry = CraftRegistry.createRegistry(registryType, registryCustom);
Class<? extends Keyed> registryType = invocation.getArgument(0);
if (registry == null) {
System.err.println("WARN: Missing registry for " + registryType);
return new DummyRegistry<>();
try {
return CraftRegistry.createRegistry(registryType, registryCustom);
} catch (Exception ignored) {
return mock(org.bukkit.Registry.class);
}
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);
@@ -245,8 +181,7 @@ public class BukkitInitialization {
// Init Enchantments
Enchantments.AQUA_AFFINITY.getClass();
initialized = true;
// Enchantment.stopAcceptingRegistrations();
}
}
@@ -267,28 +202,4 @@ 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,28 +14,22 @@
*/
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
@@ -61,6 +55,7 @@ public class PacketTypeTest {
}
public static void main(String[] args) throws Exception {
// public void generateNewPackets() throws Exception {
BukkitInitialization.initializeAll();
PacketType.onDynamicCreate = (type, className) -> {
@@ -254,16 +249,6 @@ 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() {
@@ -295,24 +280,6 @@ 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<>();
@@ -321,12 +288,6 @@ 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,7 +1,6 @@
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;
@@ -0,0 +1,53 @@
/*
* 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 static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.junit.jupiter.api.Test;
public class BlockingHashMapTest {
@Test
public void test() throws InterruptedException, ExecutionException {
final BlockingHashMap<Integer, String> map = BlockingHashMap.create();
ExecutorService service = Executors.newSingleThreadExecutor();
// Create a reader
Future<String> future = service.submit(() -> {
// Combine for easy reading
return map.get(0) + map.get(1);
});
// Wait a bit
Thread.sleep(50);
// Insert values
map.put(0, "hello ");
map.put(1, "world");
// Wait for the other thread to complete
assertEquals(future.get(), "hello world");
}
}
@@ -1,16 +1,18 @@
package com.comphenix.protocol.concurrent;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.bukkit.plugin.Plugin;
import org.junit.jupiter.api.Test;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.google.common.collect.ImmutableSet;
import org.bukkit.plugin.Plugin;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class PacketTypeListenerSetTest {
@@ -1,17 +1,20 @@
package com.comphenix.protocol.concurrent;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.junit.jupiter.api.Test;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.google.common.collect.ImmutableSet;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class PacketTypeMultiMapTest {
@@ -1,12 +1,15 @@
package com.comphenix.protocol.concurrent;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class SortedCopyOnWriteSetTest {
@Test
@@ -15,18 +15,6 @@
*/
package com.comphenix.protocol.events;
import static com.comphenix.protocol.utility.TestUtils.assertItemCollectionsEqual;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static com.comphenix.protocol.utility.TestUtils.equivalentItem;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
@@ -34,6 +22,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -41,8 +30,67 @@ import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
import net.minecraft.network.protocol.game.ServerboundPlayerCommandPacket;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.injector.PacketConstructor;
import com.comphenix.protocol.reflect.EquivalentConverter;
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.cloning.SerializableCloner;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.BlockPosition;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.ComponentConverter;
import com.comphenix.protocol.wrappers.CustomPacketPayloadWrapper;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
import com.comphenix.protocol.wrappers.EnumWrappers.EntityUseAction;
import com.comphenix.protocol.wrappers.EnumWrappers.Hand;
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
import com.comphenix.protocol.wrappers.EnumWrappers.SoundCategory;
import com.comphenix.protocol.wrappers.MovingObjectPositionBlock;
import com.comphenix.protocol.wrappers.Pair;
import com.comphenix.protocol.wrappers.PlayerInfoData;
import com.comphenix.protocol.wrappers.WrappedAttribute;
import com.comphenix.protocol.wrappers.WrappedAttributeModifier;
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.WrappedMessageSignature;
import com.comphenix.protocol.wrappers.WrappedRegistry;
import com.comphenix.protocol.wrappers.WrappedRemoteChatSessionData;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.google.common.collect.Lists;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.hover.content.Text;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket;
import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket.AttributeSnapshot;
import net.minecraft.network.protocol.game.ClientboundUpdateMobEffectPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.AttributeModifier.Operation;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.animal.CatVariant;
import net.minecraft.world.entity.animal.FrogVariant;
import org.apache.commons.lang.SerializationUtils;
import org.bukkit.ChatColor;
import org.bukkit.Material;
@@ -58,64 +106,9 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.injector.PacketConstructor;
import com.comphenix.protocol.reflect.EquivalentConverter;
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.cloning.SerializableCloner;
import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.BlockPosition;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.ComponentConverter;
import com.comphenix.protocol.wrappers.CustomPacketPayloadWrapper;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
import com.comphenix.protocol.wrappers.EnumWrappers.EntityUseAction;
import com.comphenix.protocol.wrappers.EnumWrappers.Hand;
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
import com.comphenix.protocol.wrappers.EnumWrappers.SoundCategory;
import com.comphenix.protocol.wrappers.MovingObjectPositionBlock;
import com.comphenix.protocol.wrappers.Pair;
import com.comphenix.protocol.wrappers.PlayerInfoData;
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.WrappedDataWatcher.Registry;
import com.comphenix.protocol.wrappers.WrappedEnumEntityUseAction;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedMessageSignature;
import com.comphenix.protocol.wrappers.WrappedRegistry;
import com.comphenix.protocol.wrappers.WrappedRemoteChatSessionData;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.google.common.collect.Lists;
import static com.comphenix.protocol.utility.TestUtils.*;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.hover.content.Text;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket;
import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket.AttributeSnapshot;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.ai.attributes.Attribute;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.AttributeModifier.Operation;
import net.minecraft.world.entity.animal.CatVariant;
import net.minecraft.world.entity.animal.CatVariants;
import net.minecraft.world.entity.animal.frog.FrogVariant;
import net.minecraft.world.entity.animal.frog.FrogVariants;
import static org.junit.jupiter.api.Assertions.*;
public class PacketContainerTest {
@@ -194,13 +187,13 @@ public class PacketContainerTest {
this.testPrimitive(updateTime.getLongs(), 0, (long) 0, (long) 1);
}
// @Test // TODO: Explosion no longer contains floats
@Test
public void testGetFloat() {
PacketContainer explosion = new PacketContainer(PacketType.Play.Server.EXPLOSION);
this.testPrimitive(explosion.getFloat(), 0, (float) 0, (float) 0.8);
}
// @Test // TODO: Explosion no longer contains doubles
@Test
public void testGetDoubles() {
PacketContainer explosion = new PacketContainer(PacketType.Play.Server.EXPLOSION);
this.testPrimitive(explosion.getDoubles(), 0, (double) 0, 0.8);
@@ -213,7 +206,7 @@ public class PacketContainerTest {
}
@Test
@Disabled // TODO
@Disabled // TODO ???
public void testGetStringArrays() {
PacketContainer packet = new PacketContainer(PacketType.Play.Client.UPDATE_SIGN);
this.testObjectArray(packet.getStringArrays(), 0,
@@ -237,7 +230,7 @@ public class PacketContainerTest {
@Test
public void testGetItemModifier() {
PacketContainer windowClick = new PacketContainer(PacketType.Play.Server.SET_CURSOR_ITEM);
PacketContainer windowClick = new PacketContainer(PacketType.Play.Client.WINDOW_CLICK);
ItemStack item = this.itemWithData();
@@ -330,7 +323,7 @@ public class PacketContainerTest {
assertEquals(packet.getEntityTypeModifier().read(0), EntityType.ARROW);
}
// @Test // TODO: explosion no longer contains block position list
@Test
public void testGetPositionCollectionModifier() {
PacketContainer explosionPacket = new PacketContainer(PacketType.Play.Server.EXPLOSION);
StructureModifier<List<BlockPosition>> positionAccessor = explosionPacket.getBlockPositionCollectionModifier();
@@ -349,7 +342,7 @@ public class PacketContainerTest {
}
@Test
// @Disabled // TODO -- handle type is null
// // @Disabled // TODO -- handle type is null
public void testGetDataValueCollectionModifier() {
PacketContainer entityMetadata = new PacketContainer(PacketType.Play.Server.ENTITY_METADATA);
StructureModifier<List<WrappedDataValue>> watchableAccessor = entityMetadata.getDataValueCollectionModifier();
@@ -357,8 +350,8 @@ public class PacketContainerTest {
assertEquals(0, watchableAccessor.read(0).size());
List<WrappedDataValue> values = Lists.newArrayList(
new WrappedDataValue(0, Registry.get(Byte.class), (byte) 21),
new WrappedDataValue(1, Registry.get(String.class), "World"));
new WrappedDataValue(0, WrappedDataWatcher.Registry.get(Byte.class), (byte) 21),
new WrappedDataValue(1, WrappedDataWatcher.Registry.get(String.class), "World"));
// Insert and read back
watchableAccessor.write(0, values);
@@ -386,9 +379,9 @@ public class PacketContainerTest {
}
@Test
@Disabled // TODO
// @Disabled // TODO
public void testSerialization() {
PacketContainer useItem = new PacketContainer(PacketType.Play.Client.USE_ITEM);
PacketContainer useItem = new PacketContainer(PacketType.Play.Client.USE_ITEM_ON);
useItem.getMovingBlockPositions().write(0, new MovingObjectPositionBlock(
new BlockPosition(0, 1, 0),
new Vector(0, 1, 0),
@@ -400,7 +393,7 @@ public class PacketContainerTest {
PacketContainer copy = (PacketContainer) SerializationUtils.clone(useItem);
assertEquals(PacketType.Play.Client.USE_ITEM, copy.getType());
assertEquals(PacketType.Play.Client.USE_ITEM_ON, copy.getType());
assertEquals(Hand.MAIN_HAND, copy.getHands().read(0));
assertEquals(5, copy.getIntegers().read(0));
@@ -516,45 +509,66 @@ public class PacketContainerTest {
}
@Test
@Disabled // TODO -- cloning fails
public void testAttributeList() {
PacketContainer attribute = new PacketContainer(PacketType.Play.Server.UPDATE_ATTRIBUTES);
attribute.getIntegers().write(0, 123); // Entity ID
public void testAttributeCloning() {
PacketContainer container = new PacketContainer(PacketType.Play.Server.UPDATE_ATTRIBUTES);
// Initialize some test data
List<AttributeModifier> modifiers = Lists.newArrayList(
new AttributeModifier(ResourceLocation.parse("protocollib:test"),10,
Operation.ADD_VALUE));
WrappedAttribute attribute = WrappedAttribute.newBuilder()
.attributeKey("generic.max_health")
.baseValue(20.0D)
.modifiers(Lists.newArrayList(
WrappedAttributeModifier.newBuilder()
.name("Test")
.amount(1.0D)
.operation(WrappedAttributeModifier.Operation.ADD_NUMBER)
.build()
))
.build();
// Obtain an AttributeSnapshot instance. This is complicated by the fact that AttributeSnapshots
// are inner classes (which is ultimately pointless because AttributeSnapshots don't access any
// members of the packet itself)
ClientboundUpdateAttributesPacket packet = (ClientboundUpdateAttributesPacket) attribute.getHandle();
net.minecraft.core.Registry<Attribute> registry = BuiltInRegistries.ATTRIBUTE;
Attribute base = registry.getValue(ResourceLocation.parse("max_health"));
AttributeSnapshot snapshot = new AttributeSnapshot(registry.wrapAsHolder(base), 20.0D, modifiers);
attribute.getSpecificModifier(List.class).write(0, Lists.newArrayList(snapshot));
container.getAttributeCollectionModifier().write(0, List.of(attribute));
PacketContainer cloned = attribute.deepClone();
AttributeSnapshot
clonedSnapshot = (AttributeSnapshot) cloned.getSpecificModifier(List.class).read(0).get(0);
ClientboundUpdateAttributesPacket packet = (ClientboundUpdateAttributesPacket) container.getHandle();
// Compare the fields, because apparently the packet is a field in AttributeSnapshot
for (Field field : AttributeSnapshot.class.getDeclaredFields()) {
try {
// Skip the packet
if (field.getType().equals(packet.getClass())) {
continue;
}
PacketContainer cloned = container.deepClone();
ClientboundUpdateAttributesPacket clonedPacket = (ClientboundUpdateAttributesPacket) cloned.getHandle();
field.setAccessible(true);
this.testEquality(field.get(snapshot), field.get(clonedSnapshot));
} catch (AssertionError e) {
throw e;
} catch (Throwable ex) {
ex.printStackTrace();
}
}
assertNotSame(packet, clonedPacket);
assertNotSame(packet.getValues(), clonedPacket.getValues());
List<AttributeSnapshot> values = packet.getValues();
List<AttributeSnapshot> clonedValues = clonedPacket.getValues();
assertEquals(values.size(), clonedValues.size());
assertNotSame(values.get(0), clonedValues.get(0));
AttributeSnapshot snapshot = values.get(0);
AttributeSnapshot clonedSnapshot = clonedValues.get(0);
assertEquals(Attributes.MAX_HEALTH, clonedSnapshot.attribute());
assertEquals(20.0D, clonedSnapshot.base());
assertNotSame(snapshot.modifiers(), clonedSnapshot.modifiers());
}
@Test
public void testReadAttributeList() {
AttributeInstance instance = new AttributeInstance(Attributes.MAX_HEALTH, (__) -> {});
instance.setBaseValue(20.0D);
instance.addTransientModifier(new AttributeModifier(ResourceLocation.parse("protocollib:test"), 1.0,
Operation.ADD_VALUE));
ClientboundUpdateAttributesPacket packet = new ClientboundUpdateAttributesPacket(1234, List.of(instance));
PacketContainer container = PacketContainer.fromPacket(packet);
List<WrappedAttribute> attributes = container.getAttributeCollectionModifier().read(0);
WrappedAttribute attribute = attributes.get(0);
assertEquals("generic.max_health", attribute.getAttributeKey());
assertEquals(20.0D, attribute.getBaseValue());
WrappedAttributeModifier modifier = attribute.getModifiers().iterator().next();
assertEquals(1.0D, modifier.getAmount());
assertEquals("protocollib:test", modifier.getKey().getFullKey());
assertEquals(WrappedAttributeModifier.Operation.ADD_NUMBER, modifier.getOperation());
}
@Test
@@ -586,23 +600,25 @@ public class PacketContainerTest {
effect.hasParticles());
int entityId = 42;
ClientboundUpdateMobEffectPacket packet;
// The constructor we want to call
PacketConstructor creator = PacketConstructor.DEFAULT.withPacket(
PacketType.Play.Server.ENTITY_EFFECT, new Class<?>[] { int.class, MobEffectInstance.class, boolean.class });
PacketContainer packet = creator.createPacket(entityId, mobEffect, true);
PacketContainer container = creator.createPacket(entityId, mobEffect, true);
assertEquals(entityId, packet.getIntegers().read(0));
assertEquals(effect.getAmplifier(), packet.getIntegers().read(1));
assertEquals(effect.getDuration(), packet.getIntegers().read(2));
assertEquals(entityId, container.getIntegers().read(0));
assertEquals(effect.getAmplifier(), container.getIntegers().read(1));
assertEquals(effect.getDuration(), container.getIntegers().read(2));
WrappedRegistry registry = WrappedRegistry.getRegistry(MinecraftReflection.getMobEffectListClass());
Object effectList = assertInstanceOf(
MobEffect effectList = assertInstanceOf(
MobEffect.class,
packet.getHolders(MobEffect.class, InternalStructure.CONVERTER).read(0).getHandle()
container.getHolders(MobEffect.class, InternalStructure.CONVERTER).read(0).getHandle()
);
assertEquals(effect.getType().getId(), registry.getId(effectList) + 1); // +1 is correct, see CraftPotionEffectType
assertTrue(effectList.getDescriptionId().contains("fire_resistance"));
byte b = 0;
if (effect.isAmbient()) {
@@ -617,7 +633,7 @@ public class PacketContainerTest {
b |= 8;
assertEquals(b, (byte) packet.getBytes().read(0));
assertEquals(b, (byte) container.getBytes().read(0));
}
@Test
@@ -627,6 +643,10 @@ public class PacketContainerTest {
// no change across nms versions
container.getPlayerActions().write(0, EnumWrappers.PlayerAction.OPEN_INVENTORY);
assertEquals(container.getPlayerActions().read(0), EnumWrappers.PlayerAction.OPEN_INVENTORY);
// changed in 1.15
container.getPlayerActions().write(0, EnumWrappers.PlayerAction.START_SNEAKING);
assertEquals(container.getPlayerActions().read(0), EnumWrappers.PlayerAction.START_SNEAKING);
}
@Test
@@ -667,6 +687,8 @@ public class PacketContainerTest {
@Disabled // TODO -- need a way to create a structure
public void testInternalStructures() {
PacketContainer container = new PacketContainer(PacketType.Play.Server.SCOREBOARD_TEAM);
container.getModifier().writeDefaults();
Optional<InternalStructure> optStruct = container.getOptionalStructures().read(0);
assertTrue(optStruct.isPresent());
InternalStructure struct = optStruct.get();
@@ -809,7 +831,7 @@ public class PacketContainerTest {
}
@Test
@Disabled // TODO -- can't create MAP_CHUNK packet
// @Disabled // TODO -- can't create MAP_CHUNK packet
public void testMapChunk() {
// this is a special case as we are generating a data serializer class (we only need to construct the packet)
PacketContainer container = new PacketContainer(PacketType.Play.Server.MAP_CHUNK);
@@ -897,17 +919,25 @@ public class PacketContainerTest {
}
Object buffer = MinecraftReflection.createPacketDataSerializer(0);
MinecraftMethods.getPacketWriteByteBufMethod().invoke(cloned.getHandle(), buffer);
FuzzyReflection fuzzy = FuzzyReflection.fromClass(constructed.getType().getPacketClass(), false);
FieldAccessor accessor = Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract.newBuilder()
.requireModifier(Modifier.STATIC)
.requireModifier(Modifier.PUBLIC)
.typeDerivedOf(StreamCodec.class)
.build()));
StreamCodec codec = (StreamCodec) accessor.get(cloned.getHandle());
codec.encode(buffer, cloned.getHandle());
}
private static final Set<PacketType> CLONE_EXPECTED_FAILURES = Set.of();
@Test
@Disabled // TODO -- cloning is borked
public void testCloning() {
// Try constructing all the packets
Set<PacketType> failures = new HashSet<>();
for (PacketType type : PacketType.values()) {
// TODO: try to support chat - for now chat contains to many sub classes to properly clone it
if (type.isDeprecated() || !type.isSupported() || type.name().contains("CUSTOM_PAYLOAD") || type.name().contains("CHAT")
|| type.name().contains("BUNDLE")) {
if (type.isDeprecated() || !type.isSupported()) {
continue;
}
@@ -919,21 +949,22 @@ public class PacketContainerTest {
// Make sure watchable collections can be cloned
if (type == PacketType.Play.Server.ENTITY_METADATA) {
Registry<CatVariant> catVariantRegistry = BuiltInRegistries.CAT_VARIANT;
constructed.getDataValueCollectionModifier().write(0, Lists.newArrayList(
new WrappedDataValue(0, Registry.get(Byte.class), (byte) 1),
new WrappedDataValue(0, Registry.get(Float.class), 5F),
new WrappedDataValue(0, Registry.get(String.class), "String"),
new WrappedDataValue(0, Registry.get(Boolean.class), true),
new WrappedDataValue(0, WrappedDataWatcher.Registry.get(Byte.class), (byte) 1),
new WrappedDataValue(0, WrappedDataWatcher.Registry.get(Float.class), 5F),
new WrappedDataValue(0, WrappedDataWatcher.Registry.get(String.class), "String"),
new WrappedDataValue(0, WrappedDataWatcher.Registry.get(Boolean.class), true),
new WrappedDataValue(
0,
Registry.getChatComponentSerializer(true),
0,
WrappedDataWatcher.Registry.getChatComponentSerializer(true),
Optional.of(ComponentConverter.fromBaseComponent(TEST_COMPONENT).getHandle())),
new WrappedDataValue(
0,
Registry.getItemStackSerializer(false),
WrappedDataWatcher.Registry.getItemStackSerializer(false),
BukkitConverters.getItemStackConverter().getGeneric(new ItemStack(Material.WOODEN_AXE))),
new WrappedDataValue(0, Registry.get(CatVariant.class), CatVariants.RED),
new WrappedDataValue(0, Registry.get(FrogVariant.class), FrogVariants.COLD)
new WrappedDataValue(0, WrappedDataWatcher.Registry.get(CatVariant.class), catVariantRegistry.get(CatVariant.ALL_BLACK)),
new WrappedDataValue(0, WrappedDataWatcher.Registry.get(FrogVariant.class), FrogVariant.COLD)
));
} else if (type == PacketType.Play.Server.CHAT || type == PacketType.Login.Server.DISCONNECT) {
constructed.getChatComponents().write(0, ComponentConverter.fromBaseComponent(TEST_COMPONENT));
@@ -942,7 +973,7 @@ public class PacketContainerTest {
} else if (type == PacketType.Play.Server.GAME_STATE_CHANGE) {
constructed.getStructures().write(
0,
InternalStructure.getConverter().getSpecific(ClientboundGameEventPacket.WIN_GAME));
InternalStructure.getConverter().getSpecific(ClientboundGameEventPacket.ARROW_HIT_PLAYER));
} else if (type == PacketType.Play.Client.USE_ITEM || type == PacketType.Play.Client.BLOCK_PLACE) {
constructed.getLongs().write(0, 0L); // timestamp of the packet, not sent over the network
}
@@ -962,10 +993,13 @@ public class PacketContainerTest {
serializedCloned.getLongs().write(0, 0L);
}
this.assertPacketsEqualAndSerializable(constructed, serializedCloned);
} catch (Throwable t) {
Assertions.fail("Unable to clone " + type, t);
} catch (Throwable ex) {
failures.add(type);
ex.printStackTrace();
}
}
assertEquals(CLONE_EXPECTED_FAILURES, failures);
}
// Convert to objects that support equals()
@@ -1,7 +1,11 @@
package com.comphenix.protocol.events;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.when;
import java.util.UUID;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.junit.jupiter.api.BeforeEach;
@@ -9,9 +13,6 @@ import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
public class SerializedOfflinePlayerTest {
private static final String name = "playerName";
@@ -1,27 +1,8 @@
package com.comphenix.protocol.injector;
import static com.comphenix.protocol.utility.TestUtils.setFinalField;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.lang.reflect.Field;
import org.bukkit.craftbukkit.v1_21_R5.CraftWorld;
import org.bukkit.craftbukkit.v1_21_R5.entity.CraftEntity;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.ai.behavior.EntityTracker;
import org.junit.jupiter.api.BeforeAll;
public class EntityUtilitiesTest {
@@ -30,18 +11,19 @@ public class EntityUtilitiesTest {
BukkitInitialization.initializeAll();
}
/*
@Test
public void testReflection() {
CraftWorld bukkit = mock(CraftWorld.class);
ServerLevel world = mock(ServerLevel.class);
when(bukkit.getHandle()).thenReturn(world);
ServerChunkCache provider = mock(ServerChunkCache.class);
when(world.getChunkSource()).thenReturn(provider);
ChunkProviderServer provider = mock(ChunkProviderServer.class);
when(world.l()).thenReturn(provider);
ChunkMap chunkMap = mock(ChunkMap.class);
Field chunkMapField = FuzzyReflection.fromClass(ServerChunkCache.class, true)
.getField(FuzzyFieldContract.newBuilder().typeExact(ChunkMap.class).build());
PlayerChunkMap chunkMap = mock(PlayerChunkMap.class);
Field chunkMapField = FuzzyReflection.fromClass(ChunkProviderServer.class, true)
.getField(FuzzyFieldContract.newBuilder().typeExact(PlayerChunkMap.class).build());
setFinalField(provider, chunkMapField, chunkMap);
CraftEntity bukkitEntity = mock(CraftEntity.class);
@@ -55,8 +37,9 @@ public class EntityUtilitiesTest {
Int2ObjectMap<EntityTracker> trackerMap = new Int2ObjectOpenHashMap<>();
trackerMap.put(1, tracker);
Field trackedEntitiesField = FuzzyReflection.fromClass(ChunkMap.class, true)
Field trackedEntitiesField = FuzzyReflection.fromClass(PlayerChunkMap.class, true)
.getField(FuzzyFieldContract.newBuilder().typeExact(Int2ObjectMap.class).build());
setFinalField(chunkMap, trackedEntitiesField, trackerMap);
}
*/
}
@@ -1,12 +1,17 @@
package com.comphenix.protocol.injector;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.comphenix.protocol.injector.PluginVerifier.VerificationResult;
import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.comphenix.protocol.injector.PluginVerifier.VerificationResult;
import com.google.common.base.Objects;
import org.bukkit.Server;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
@@ -16,11 +21,6 @@ import org.junit.jupiter.api.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
// Damn final classes
class PluginVerifierTest {
@@ -3,21 +3,19 @@
*/
package com.comphenix.protocol.injector;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.netty.WirePacket;
import io.netty.buffer.ByteBuf;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @author dmulloy2
*/
@@ -1,7 +1,6 @@
package com.comphenix.protocol.injector.netty.channel;
import com.comphenix.protocol.BukkitInitialization;
import org.junit.jupiter.api.BeforeAll;
public class ChannelProtocolUtilTest {
@@ -1,21 +0,0 @@
package com.comphenix.protocol.injector.netty.channel;
import com.comphenix.protocol.BukkitInitialization;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
public class PacketListenerInvokerTest {
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
}
@Test
public void testInitialization() {
assertDoesNotThrow(() -> PacketListenerInvoker.ensureStaticInitializedWithoutError());
}
}
@@ -6,6 +6,8 @@ import java.util.List;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import net.minecraft.network.protocol.game.ServerboundUseItemPacket;
import net.minecraft.network.protocol.login.LoginPacketTypes;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -1,5 +1,8 @@
package com.comphenix.protocol.injector.temporary;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.junit.jupiter.api.BeforeEach;
@@ -7,12 +10,14 @@ import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class TemporaryPlayerFactoryTest {
private static final TemporaryPlayerFactory temporaryPlayerFactory = new TemporaryPlayerFactory();
@Mock
Server server;
@Mock
MinimalInjector minimalInjector;
@BeforeEach
public void initMocks() {
@@ -21,7 +26,17 @@ public class TemporaryPlayerFactoryTest {
@Test
public void testUnavailableSocketInjector() {
Player player = TemporaryPlayerFactory.createTemporaryPlayer();
Player player = temporaryPlayerFactory.createTemporaryPlayer(this.server);
assertThrows(IllegalStateException.class, player::getPlayer);
}
@Test
public void createTemporaryPlayer() {
Player player = temporaryPlayerFactory.createTemporaryPlayer(this.server, this.minimalInjector);
assertEquals(this.server, player.getServer());
// May seem dumb, but this makes sure that the .equals method is still instact.
assertEquals(player, player);
}
}
@@ -1,11 +1,10 @@
package com.comphenix.protocol.reflect.accessors;
import java.lang.reflect.Field;
import com.comphenix.protocol.reflect.ExactReflection;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Field;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -1,15 +1,18 @@
package com.comphenix.protocol.reflect.cloning;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import java.util.Arrays;
import java.util.List;
import net.minecraft.core.NonNullList;
import net.minecraft.world.item.ItemStack;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class AggregateClonerTest {
@BeforeAll
@@ -1,36 +0,0 @@
package com.comphenix.protocol.reflect.instances;
import java.util.UUID;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import net.minecraft.core.NonNullList;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.ItemStack;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class MinecraftGeneratorTest {
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
}
@Test
public void testInstantiation() {
assertNotNull(MinecraftGenerator.INSTANCE.create(UUID.class));
assertNotNull(MinecraftGenerator.INSTANCE.create(PacketType.Protocol.class));
assertNotNull(MinecraftGenerator.INSTANCE.create(ItemStack.class));
assertNotNull(MinecraftGenerator.INSTANCE.create(EntityType.class));
assertNotNull(MinecraftGenerator.INSTANCE.create(Int2ObjectMap.class));
assertNotNull(MinecraftGenerator.INSTANCE.create(Int2ObjectOpenHashMap.class));
assertNotNull(MinecraftGenerator.INSTANCE.create(NonNullList.class));
}
}
@@ -3,21 +3,19 @@
*/
package com.comphenix.protocol.updater;
import java.util.logging.Logger;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.comphenix.protocol.updater.Updater.UpdateType;
import java.util.logging.Logger;
import org.bukkit.Server;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @author dmulloy2
*/
@@ -1,14 +1,12 @@
package com.comphenix.protocol.utility;
import java.lang.reflect.Field;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import com.comphenix.protocol.BukkitInitialization;
import java.lang.reflect.Field;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class MinecraftMethodsTest {
@BeforeAll
@@ -18,12 +16,8 @@ public class MinecraftMethodsTest {
@Test
public void testSendPacketMethods() {
assertNotNull(MinecraftMethods.getPlayerConnectionSendMethod());
assertNotNull(MinecraftMethods.getPlayerConnectionDisconnectMethod());
assertNotNull(MinecraftMethods.getNetworkManagerSendMethod());
assertNotNull(MinecraftMethods.getNetworkManagerReadPacketMethod());
assertNotNull(MinecraftMethods.getNetworkManagerDisconnectMethod());
assertNotNull(MinecraftMethods.getSendPacketMethod());
assertNotNull(MinecraftMethods.getNetworkManagerHandleMethod());
}
private void setNull(final String fieldName) throws NoSuchFieldException, IllegalAccessException {
@@ -3,19 +3,9 @@ package com.comphenix.protocol.utility;
import com.comphenix.protocol.BukkitInitialization;
import com.mojang.authlib.GameProfile;
import net.minecraft.nbt.NbtIo;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.ComponentSerialization;
import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket;
import net.minecraft.network.protocol.status.ServerStatus;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.util.Crypt;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.v1_21_R5.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.junit.jupiter.api.AfterAll;
@@ -62,67 +52,68 @@ public class MinecraftReflectionTest {
assertNull(MinecraftReflection.getNullableNMS("ProtocolLib"));
}
/*
@Test
public void testAttributeSnapshot() {
assertEquals(ClientboundUpdateAttributesPacket.AttributeSnapshot.class,
assertEquals(PacketPlayOutUpdateAttributes.AttributeSnapshot.class,
MinecraftReflection.getAttributeSnapshotClass());
}
@Test
public void testChatComponent() {
assertEquals(Component.class, MinecraftReflection.getIChatBaseComponentClass());
assertEquals(IChatBaseComponent.class, MinecraftReflection.getIChatBaseComponentClass());
}
@Test
public void testChatSerializer() {
assertEquals(ComponentSerialization.class, MinecraftReflection.getChatSerializerClass());
assertEquals(IChatBaseComponent.ChatSerializer.class, MinecraftReflection.getChatSerializerClass());
}
@Test
public void testChunkCoordIntPair() {
assertEquals(ChunkPos.class, MinecraftReflection.getChunkCoordIntPair());
assertEquals(ChunkCoordIntPair.class, MinecraftReflection.getChunkCoordIntPair());
}
@Test
public void testIBlockData() {
assertEquals(BlockState.class, MinecraftReflection.getIBlockDataClass());
assertEquals(IBlockData.class, MinecraftReflection.getIBlockDataClass());
}
@Test
public void testPlayerConnection() {
assertEquals(ServerGamePacketListenerImpl.class, MinecraftReflection.getPlayerConnectionClass());
assertEquals(PlayerConnection.class, MinecraftReflection.getPlayerConnectionClass());
}
@Test
public void testServerPing() {
assertEquals(ServerStatus.class, MinecraftReflection.getServerPingClass());
assertEquals(ServerPing.class, MinecraftReflection.getServerPingClass());
}
@Test
public void testServerPingPlayerSample() {
assertEquals(ServerStatus.Players.class, MinecraftReflection.getServerPingPlayerSampleClass());
assertEquals(ServerPing.ServerPingPlayerSample.class, MinecraftReflection.getServerPingPlayerSampleClass());
}
@Test
public void testServerPingServerData() {
assertEquals(ServerStatus.Version.class, MinecraftReflection.getServerPingServerDataClass());
assertEquals(ServerPing.ServerData.class, MinecraftReflection.getServerPingServerDataClass());
}
@Test
public void testNbtStreamTools() {
assertEquals(NbtIo.class, MinecraftReflection.getNbtCompressedStreamToolsClass());
assertEquals(NBTCompressedStreamTools.class, MinecraftReflection.getNbtCompressedStreamToolsClass());
}
@Test
public void testDataWatcherItem() {
assertEquals(SynchedEntityData.DataItem.class, MinecraftReflection.getDataWatcherItemClass());
assertEquals(DataWatcher.Item.class, MinecraftReflection.getDataWatcherItemClass());
}
@Test
public void testLoginSignature() {
assertEquals(Crypt.SaltSignaturePair.class, MinecraftReflection.getSaltedSignatureClass());
assertEquals(MinecraftEncryption.b.class, MinecraftReflection.getSaltedSignatureClass());
}
*/
@Test
public void testItemStacks() {
ItemStack stack = new ItemStack(Material.GOLDEN_SWORD);
@@ -2,8 +2,8 @@ package com.comphenix.protocol.utility;
public class MinecraftReflectionTestUtil {
public static final String RELEASE_TARGET = "1.21.6";
public static final String PACKAGE_VERSION = "v1_21_R5";
public static final String RELEASE_TARGET = "1.21.0";
public static final String PACKAGE_VERSION = "v1_21_R1";
public static final String NMS = "net.minecraft";
public static final String OBC = "org.bukkit.craftbukkit." + PACKAGE_VERSION;
@@ -1,12 +0,0 @@
package com.comphenix.protocol.utility;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class MinecraftRegistryAccessTest {
@Test
public void testGet() {
assertNotNull(MinecraftRegistryAccess.get());
}
}
@@ -48,7 +48,7 @@ class MinecraftVersionTest {
@Test
void testCurrent() {
assertEquals(MinecraftVersion.v1_21_6, MinecraftVersion.getCurrentVersion());
assertEquals(MinecraftVersion.v1_21_0, MinecraftVersion.getCurrentVersion());
}
@Test
@@ -1,14 +1,13 @@
package com.comphenix.protocol.utility;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class SnapshotVersionTest {
@Test
@@ -15,11 +15,13 @@ import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class StreamSerializerTest {
@@ -37,7 +39,10 @@ public class StreamSerializerTest {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
serializer.serializeString(new DataOutputStream(buffer), initial);
DataInputStream input = new DataInputStream(new ByteArrayInputStream(buffer.toByteArray()));
byte[] bytes = buffer.toByteArray();
assertTrue(bytes.length >= initial.length());
DataInputStream input = new DataInputStream(new ByteArrayInputStream(bytes));
String deserialized = serializer.deserializeString(input, 50);
assertEquals(initial, deserialized);
@@ -62,6 +67,7 @@ public class StreamSerializerTest {
}
@Test
@Disabled // TODO -- replaced with stream codecs
public void testItems() throws IOException {
StreamSerializer serializer = new StreamSerializer();
ItemStack initial = new ItemStack(Material.STRING);
@@ -73,6 +79,7 @@ public class StreamSerializerTest {
}
@Test
@Disabled // TODO -- replaced with stream codecs
public void testItemMeta() throws IOException {
StreamSerializer serializer = new StreamSerializer();
ItemStack initial = new ItemStack(Material.BLUE_WOOL, 2);
@@ -1,5 +1,11 @@
package com.comphenix.protocol.utility;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.comphenix.protocol.reflect.accessors.Accessors;
import java.lang.reflect.Field;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
@@ -8,15 +14,11 @@ import java.util.List;
import java.util.Random;
import java.util.UUID;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey;
import com.comphenix.protocol.wrappers.WrappedRemoteChatSessionData;
import org.bukkit.Bukkit;
import org.bukkit.inventory.ItemStack;
import static org.junit.jupiter.api.Assertions.*;
public class TestUtils {
public static void assertItemCollectionsEqual(List<ItemStack> first, List<ItemStack> second) {
@@ -1,31 +1,25 @@
package com.comphenix.protocol.wrappers;
import java.util.Optional;
import static com.comphenix.protocol.utility.MinecraftReflection.getMinecraftClass;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
import java.util.Optional;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.utility.MinecraftReflection;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.utility.MinecraftReflection;
import net.minecraft.advancements.AdvancementType;
import net.minecraft.advancements.DisplayInfo;
import net.minecraft.core.ClientAsset;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.contents.PlainTextContents;
import net.minecraft.resources.ResourceLocation;
public class AutoWrapperTest {
/*
@BeforeAll
public static void initializeBukkit() {
BukkitInitialization.initializeAll();
@@ -37,7 +31,7 @@ public class AutoWrapperTest {
display.title = WrappedChatComponent.fromText("Test123");
display.description = WrappedChatComponent.fromText("Test567");
display.item = new ItemStack(Material.GOLD_INGOT);
display.background = Optional.of(new ClientAsset(ResourceLocation.fromNamespaceAndPath("minecraft", "test")));
display.background = Optional.of(new MinecraftKey("test"));
display.frameType = WrappedFrameType.CHALLENGE;
display.announceChat = false;
display.showToast = true;
@@ -45,34 +39,34 @@ public class AutoWrapperTest {
display.x = 5f;
display.y = 67f;
DisplayInfo nms = (DisplayInfo) displayWrapper().unwrap(display);
AdvancementDisplay nms = (AdvancementDisplay) displayWrapper().unwrap(display);
assertTrue(nms.shouldShowToast());
assertTrue(nms.isHidden());
assertFalse(nms.shouldAnnounceChat());
assertTrue(nms.getBackground().isPresent());
assertEquals("test", nms.getBackground().get().id().getPath());
validateRawText(nms.getTitle(), "Test123");
validateRawText(nms.getDescription(), "Test567");
assertSame(AdvancementType.CHALLENGE, nms.getType());
assertSame(MinecraftReflection.getBukkitItemStack(nms.getIcon()).getType(), Material.GOLD_INGOT);
assertEquals(5f, nms.getX(), 0f);
assertEquals(67f, nms.getY(), 0f);
assertTrue(nms.h());
assertTrue(nms.j());
assertFalse(nms.i());
assertTrue(nms.d().isPresent());
assertEquals("test", nms.d().get().a());
validateRawText(nms.a(), "Test123");
validateRawText(nms.b(), "Test567");
assertSame(AdvancementFrameType.b, nms.e());
assertSame(MinecraftReflection.getBukkitItemStack(nms.c()).getType(), Material.GOLD_INGOT);
assertEquals(5f, nms.f(), 0f);
assertEquals(67f, nms.g(), 0f);
}
@Test
public void testFromNms() {
DisplayInfo display = new DisplayInfo(
AdvancementDisplay display = new AdvancementDisplay(
(net.minecraft.world.item.ItemStack)MinecraftReflection.getMinecraftItemStack(new ItemStack(Material.ENDER_EYE)),
Component.literal("Test123"),
Component.literal("Test567"),
Optional.of(new ClientAsset(ResourceLocation.fromNamespaceAndPath("minecraft", "test"))),
AdvancementType.CHALLENGE,
IChatBaseComponent.b("Test123"),
IChatBaseComponent.b("Test567"),
Optional.of(new net.minecraft.resources.MinecraftKey("minecraft", "test")),
AdvancementFrameType.b,
true,
false,
true
);
display.setLocation(5f, 67f);
display.a(5f, 67f);
WrappedAdvancementDisplay wrapped = displayWrapper().wrap(display);
@@ -80,7 +74,7 @@ public class AutoWrapperTest {
assertTrue(wrapped.hidden);
assertFalse(wrapped.announceChat);
assertTrue(wrapped.background.isPresent());
assertEquals("test", wrapped.background.get().id().getPath());
assertEquals("test", wrapped.background.get().getKey());
assertEquals("\"Test123\"", wrapped.title.getJson());
assertEquals("\"Test567\"", wrapped.description.getJson());
assertSame(WrappedFrameType.CHALLENGE, wrapped.frameType);
@@ -95,16 +89,14 @@ public class AutoWrapperTest {
.field(0, BukkitConverters.getWrappedChatComponentConverter())
.field(1, BukkitConverters.getWrappedChatComponentConverter())
.field(2, BukkitConverters.getItemStackConverter())
.field(3, Converters.optional(Converters.passthrough(ClientAsset.class)))
.field(4, EnumWrappers.getGenericConverter(
getMinecraftClass("advancements.AdvancementType", "advancements.AdvancementFrameType", "advancements.FrameType"),
WrappedFrameType.class)
);
.field(3, Converters.optional(MinecraftKey.getConverter()))
.field(4, EnumWrappers.getGenericConverter(getMinecraftClass("advancements.AdvancementFrameType", "advancements.FrameType"),
WrappedFrameType.class));
}
private void validateRawText(Component component, String expected) {
PlainTextContents.LiteralContents content = assertInstanceOf(PlainTextContents.LiteralContents.class, component.getContents());
assertEquals(expected, content.text());
private void validateRawText(IChatBaseComponent component, String expected) {
LiteralContents content = assertInstanceOf(LiteralContents.class, component.b());
assertEquals(expected, content.b());
}
public enum WrappedFrameType {
@@ -118,12 +110,12 @@ public class AutoWrapperTest {
public WrappedChatComponent title;
public WrappedChatComponent description;
public ItemStack item;
public Optional<ClientAsset> background;
public Optional<MinecraftKey> background;
public WrappedFrameType frameType;
public boolean showToast;
public boolean announceChat;
public boolean hidden;
public float x;
public float y;
}
}*/
}
@@ -30,7 +30,7 @@ public class BukkitConvertersTest {
}
@Test
@Disabled // TODO -- enchantment cannot be applied to this itemstack(???)
// @Disabled // TODO -- enchantment cannot be applied to this itemstack(???)
public void testItemStacks() {
ItemStack item = new ItemStack(Material.DIAMOND_SWORD, 16);
item.addEnchantment(Enchantment.SHARPNESS, 4);
Loaded 100 of 127 files, more files were not shown because too many files have changed in this diff. Show more