Compare commits

...

24 Commits

Author SHA1 Message Date
Maurice Eisenblätter b0c4b7fe45
Add support for 1.20.5/1.20.6 part 2 (#2910) 2024-05-06 19:02:15 +02:00
Maurice Eisenblätter a2f02652e9
Fix possible deadlock in async packet processing (#2545)
fix: start processing further packets if a packet is done
2024-05-06 11:27:16 +02:00
Dan Mulloy f9d3266777
WIP: Compile against 1.20.5 2024-04-27 22:46:28 -05:00
Maurice Eisenblätter 70e4812fde
Initial Support for 1.20.5 (#2894) 2024-04-27 22:21:16 -05:00
Dan Mulloy d0c7382d7f
Bump version to 5.2.1 2024-04-27 21:55:52 -05:00
Dan Mulloy e1255edb32
Fix build 2024-04-07 11:18:58 -05:00
Dan Mulloy 6f057b361b
Bump version to 5.2.0 for release 2024-04-07 10:04:32 -05:00
Lukas Alt 80aa420099
Fixed setting Base64 favicon for 1.19.4 or later (#2533)
Fix redundant base64 encoding of favicon
2023-12-19 15:53:16 +00:00
Nick 564349c914
Fallback to the HANDSHAKING protocol if no packet type is found in the registry (fixes https://github.com/dmulloy2/ProtocolLib/issues/2601) 2023-12-16 11:12:57 -06:00
TrainmasterHD 0da27515a4
Finish 1.20.4 update (#2683)
* update PacketTypes

* add new enum values for EnumWrappers and add ScoreboardAction to known invalids as it was removed

* make MinecraftKey optional in AutoWrapperTest

* update adventure dependencies to 4.14.0

* fix typo in maximum minecraft version

* add chat component for disconnect packet when running clone test

* adjust chat components to new component structure
2023-12-16 08:47:22 -06:00
Richy 2448d8372e
Cache if a class has a default instance (#2676) 2023-12-11 08:07:10 -06:00
Dan Mulloy 0eca2eebd2
Bump version to 5.2.0-SNAPSHOT 2023-12-09 16:09:29 -06:00
Dan Mulloy 8ba1dc1284
Start 1.20.4 update 2023-12-09 15:56:45 -06:00
Pasqual Koschmieder 80a097953f
update dependencies & gradle (#2589) 2023-10-25 07:07:58 -05:00
Maurice Eisenblätter d4b4f50674
Fix async handler scheduler support for folia (#2531) 2023-10-24 20:10:35 -05:00
Pasqual Koschmieder a7aa31adc0
improve support for custom payloads in 1.20.2 (#2553) 2023-10-25 01:01:35 +00:00
Pasqual Koschmieder af33a2ab41
fix invalid packet types due to state mismatch when calling packet events (#2568) 2023-10-24 19:56:38 -05:00
Pasqual Koschmieder 03d7be13d0
Update for 1.20.2 (#2501) 2023-09-23 16:21:07 -05:00
Maurice Eisenblätter f0401acd2f
Improve async packet processing logic (#2503)
* fix: enqueue async packet event after processing delay is zero
* fix: async packet processing
2023-08-27 10:11:37 -05:00
Fanfaryy 2686c9fec0
Fixed PING packet for 1.19.1 and 1.19.2 (#2518)
Changed PLAYER_CHAT_HEADER duplicated currentId due to bug on 1.19.1 and 1.19.2 versions with PING
2023-08-26 21:00:57 +00:00
Lukas Alt 8fc5e509ae
Added factory method for initializing WrappedDataValue (#2523) 2023-08-26 15:49:11 -05:00
Manuel P 9e9b39a37d
Fix PacketContainer serialization (#2479)
Fixes #2478
2023-08-05 19:06:09 +00:00
Tomescu Vlad-Costin e219103a25
Added a null check for the optional converter (#2485) 2023-08-05 19:02:41 +00:00
Dan Mulloy 98fbcc6585
Update version to 5.1.1 for development 2023-08-05 13:58:29 -05:00
79 changed files with 2747 additions and 1135 deletions

View File

@ -15,7 +15,7 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '19'
java-version: '21'
cache: 'gradle'
- name: Run gradle build lifecycle

View File

@ -26,7 +26,7 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '19'
java-version: '21'
cache: 'gradle'
- name: Initialize CodeQL

View File

@ -53,7 +53,7 @@ You can also add ProtocolLib as a Maven dependency:
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>5.0.0</version>
<version>5.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
@ -67,7 +67,7 @@ repositories {
}
dependencies {
compileOnly 'com.comphenix.protocol:ProtocolLib:5.0.0'
compileOnly 'com.comphenix.protocol:ProtocolLib:5.1.0'
}
```

View File

@ -5,7 +5,7 @@ plugins {
}
group = 'com.comphenix.protocol'
version = '5.1.0'
version = '5.2.1-SNAPSHOT'
description = 'Provides access to the Minecraft protocol'
def isSnapshot = version.endsWith('-SNAPSHOT')
@ -33,22 +33,22 @@ repositories {
}
dependencies {
implementation 'net.bytebuddy:byte-buddy:1.14.3'
compileOnly 'org.spigotmc:spigot-api:1.20-R0.1-SNAPSHOT'
compileOnly 'org.spigotmc:spigot:1.20-R0.1-SNAPSHOT'
implementation 'net.bytebuddy:byte-buddy:1.14.14'
compileOnly 'org.spigotmc:spigot-api:1.20.5-R0.1-SNAPSHOT'
compileOnly 'org.spigotmc:spigot:1.20.5-R0.1-SNAPSHOT'
compileOnly 'io.netty:netty-all:4.0.23.Final'
compileOnly 'net.kyori:adventure-text-serializer-gson:4.13.0'
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.9.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2'
testImplementation 'org.mockito:mockito-core:4.11.0'
testImplementation 'org.mockito:mockito-inline:4.11.0'
testImplementation 'io.netty:netty-common:4.1.77.Final'
testImplementation 'io.netty:netty-transport:4.1.77.Final'
testImplementation 'org.spigotmc:spigot:1.20-R0.1-SNAPSHOT'
testImplementation 'net.kyori:adventure-text-serializer-gson:4.13.0'
testImplementation 'net.kyori:adventure-text-serializer-plain:4.13.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.20.5-R0.1-SNAPSHOT'
testImplementation 'net.kyori:adventure-text-serializer-gson:4.14.0'
testImplementation 'net.kyori:adventure-text-serializer-plain:4.14.0'
}
java {
@ -69,6 +69,9 @@ shadowJar {
test {
useJUnitPlatform()
testLogging {
exceptionFormat = 'full'
}
}
processResources {

Binary file not shown.

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

17
gradlew vendored
View File

@ -83,7 +83,8 @@ done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@ -201,11 +202,11 @@ fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \

View File

@ -5,8 +5,17 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.*;
import java.util.function.Consumer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
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;
@ -17,9 +26,6 @@ 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>
@ -29,7 +35,7 @@ import org.bukkit.Bukkit;
public class PacketType implements Serializable, Cloneable, Comparable<PacketType> {
// Increment whenever the type changes
private static final long serialVersionUID = 1L;
/**
* Represents an unknown packet ID.
*/
@ -101,117 +107,128 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static class Server extends PacketTypeEnum {
private static final Sender SENDER = Sender.SERVER;
public static final PacketType BUNDLE = new PacketType(PROTOCOL, SENDER, 0x00, "Delimiter", "BundleDelimiterPacket");
public static final PacketType BUNDLE = new PacketType(PROTOCOL, SENDER, 0x00, "BundleDelimiter", "Delimiter", "BundleDelimiterPacket");
public static final PacketType SPAWN_ENTITY = new PacketType(PROTOCOL, SENDER, 0x01, "SpawnEntity", "SPacketSpawnObject");
public static final PacketType SPAWN_ENTITY_EXPERIENCE_ORB = new PacketType(PROTOCOL, SENDER, 0x02, "SpawnEntityExperienceOrb", "SPacketSpawnExperienceOrb");
public static final PacketType NAMED_ENTITY_SPAWN = new PacketType(PROTOCOL, SENDER, 0x03, "NamedEntitySpawn", "SPacketSpawnPlayer");
public static final PacketType ANIMATION = new PacketType(PROTOCOL, SENDER, 0x04, "Animation", "SPacketAnimation");
public static final PacketType STATISTIC = new PacketType(PROTOCOL, SENDER, 0x05, "Statistic", "SPacketStatistics");
public static final PacketType BLOCK_CHANGED_ACK = new PacketType(PROTOCOL, SENDER, 0x06, "BlockChangedAck");
public static final PacketType BLOCK_BREAK_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x07, "BlockBreakAnimation", "SPacketBlockBreakAnim");
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x08, "TileEntityData", "SPacketUpdateTileEntity");
public static final PacketType BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x09, "BlockAction", "SPacketBlockAction");
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x0A, "BlockChange", "SPacketBlockChange");
public static final PacketType BOSS = new PacketType(PROTOCOL, SENDER, 0x0B, "Boss", "SPacketUpdateBossInfo");
public static final PacketType SERVER_DIFFICULTY = new PacketType(PROTOCOL, SENDER, 0x0C, "ServerDifficulty", "SPacketServerDifficulty");
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, "TabComplete", "SPacketTabComplete");
public static final PacketType COMMANDS = new PacketType(PROTOCOL, SENDER, 0x10, "Commands");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x11, "CloseWindow", "SPacketCloseWindow");
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x12, "WindowItems", "SPacketWindowItems");
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x13, "WindowData", "SPacketWindowProperty");
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x14, "SetSlot", "SPacketSetSlot");
public static final PacketType SET_COOLDOWN = new PacketType(PROTOCOL, SENDER, 0x15, "SetCooldown", "SPacketCooldown");
public static final PacketType CUSTOM_CHAT_COMPLETIONS = new PacketType(PROTOCOL, SENDER, 0x16, "CustomChatCompletions");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x17, "CustomPayload", "SPacketCustomPayload");
public static final PacketType DAMAGE_EVENT = new PacketType(PROTOCOL, SENDER, 0x18, "DamageEvent", "ClientboundDamageEventPacket");
public static final PacketType DELETE_CHAT_MESSAGE = new PacketType(PROTOCOL, SENDER, 0x19, "DeleteChat");
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x1A, "KickDisconnect", "SPacketDisconnect");
public static final PacketType DISGUISED_CHAT = new PacketType(PROTOCOL, SENDER, 0x1B, "DisguisedChat");
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x1C, "EntityStatus", "SPacketEntityStatus");
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x1D, "Explosion", "SPacketExplosion");
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x1E, "UnloadChunk", "SPacketUnloadChunk");
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x1F, "GameStateChange", "SPacketChangeGameState");
public static final PacketType OPEN_WINDOW_HORSE = new PacketType(PROTOCOL, SENDER, 0x20, "OpenWindowHorse");
public static final PacketType HURT_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x21, "HurtAnimation", "ClientboundHurtAnimationPacket");
public static final PacketType INITIALIZE_BORDER = new PacketType(PROTOCOL, SENDER, 0x22, "InitializeBorder");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x23, "KeepAlive", "SPacketKeepAlive");
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x24, "LevelChunkWithLight", "MapChunk", "SPacketChunkData");
public static final PacketType WORLD_EVENT = new PacketType(PROTOCOL, SENDER, 0x25, "WorldEvent", "SPacketEffect");
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x26, "WorldParticles", "SPacketParticles");
public static final PacketType LIGHT_UPDATE = new PacketType(PROTOCOL, SENDER, 0x27, "LightUpdate");
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x28, "Login", "SPacketJoinGame");
public static final PacketType MAP = new PacketType(PROTOCOL, SENDER, 0x29, "Map", "SPacketMaps");
public static final PacketType OPEN_WINDOW_MERCHANT = new PacketType(PROTOCOL, SENDER, 0x2A, "OpenWindowMerchant");
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x2B, "Entity$RelEntityMove", "Entity$PacketPlayOutRelEntityMove");
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x2C, "Entity$RelEntityMoveLook", "Entity$PacketPlayOutRelEntityMoveLook");
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x2D, "Entity$EntityLook", "Entity$PacketPlayOutEntityLook");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x2E, "VehicleMove", "SPacketMoveVehicle");
public static final PacketType OPEN_BOOK = new PacketType(PROTOCOL, SENDER, 0x2F, "OpenBook");
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x30, "OpenWindow", "SPacketOpenWindow");
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x31, "OpenSignEditor", "SPacketSignEditorOpen");
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x32, "Ping");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x33, "AutoRecipe", "SPacketPlaceGhostRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x34, "Abilities", "SPacketPlayerAbilities");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x35, "PlayerChat", "Chat", "SPacketChat");
public static final PacketType PLAYER_COMBAT_END = new PacketType(PROTOCOL, SENDER, 0x36, "PlayerCombatEnd");
public static final PacketType PLAYER_COMBAT_ENTER = new PacketType(PROTOCOL, SENDER, 0x37, "PlayerCombatEnter");
public static final PacketType PLAYER_COMBAT_KILL = new PacketType(PROTOCOL, SENDER, 0x38, "PlayerCombatKill");
public static final PacketType PLAYER_INFO_REMOVE = new PacketType(PROTOCOL, SENDER, 0x39, "PlayerInfoRemove");
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x3A, "PlayerInfoUpdate", "PlayerInfo");
public static final PacketType LOOK_AT = new PacketType(PROTOCOL, SENDER, 0x3B, "LookAt", "SPacketPlayerPosLook");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x3C, "Position");
public static final PacketType RECIPES = new PacketType(PROTOCOL, SENDER, 0x3D, "Recipes", "SPacketRecipeBook");
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x3E, "EntityDestroy", "SPacketDestroyEntities");
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x3F, "RemoveEntityEffect", "SPacketRemoveEntityEffect");
public static final PacketType RESOURCE_PACK_SEND = new PacketType(PROTOCOL, SENDER, 0x40, "ResourcePackSend", "SPacketResourcePackSend");
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x41, "Respawn", "SPacketRespawn");
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x42, "EntityHeadRotation", "SPacketEntityHeadLook");
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x43, "MultiBlockChange", "SPacketMultiBlockChange");
public static final PacketType SELECT_ADVANCEMENT_TAB = new PacketType(PROTOCOL, SENDER, 0x44, "SelectAdvancementTab", "SPacketSelectAdvancementsTab");
public static final PacketType SERVER_DATA = new PacketType(PROTOCOL, SENDER, 0x45, "ServerData");
public static final PacketType SET_ACTION_BAR_TEXT = new PacketType(PROTOCOL, SENDER, 0x46, "SetActionBarText");
public static final PacketType SET_BORDER_CENTER = new PacketType(PROTOCOL, SENDER, 0x47, "SetBorderCenter");
public static final PacketType SET_BORDER_LERP_SIZE = new PacketType(PROTOCOL, SENDER, 0x48, "SetBorderLerpSize");
public static final PacketType SET_BORDER_SIZE = new PacketType(PROTOCOL, SENDER, 0x49, "SetBorderSize");
public static final PacketType SET_BORDER_WARNING_DELAY = new PacketType(PROTOCOL, SENDER, 0x4A, "SetBorderWarningDelay");
public static final PacketType SET_BORDER_WARNING_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x4B, "SetBorderWarningDistance");
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x4C, "Camera", "SPacketCamera");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x4D, "HeldItemSlot", "SPacketHeldItemChange");
public static final PacketType VIEW_CENTRE = new PacketType(PROTOCOL, SENDER, 0x4E, "ViewCentre");
public static final PacketType VIEW_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x4F, "ViewDistance");
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x50, "SpawnPosition", "SPacketSpawnPosition");
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x51, "ScoreboardDisplayObjective", "SPacketDisplayObjective");
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x52, "EntityMetadata", "SPacketEntityMetadata");
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x53, "AttachEntity", "SPacketEntityAttach");
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x54, "EntityVelocity", "SPacketEntityVelocity");
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x55, "EntityEquipment", "SPacketEntityEquipment");
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x56, "Experience", "SPacketSetExperience");
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x57, "UpdateHealth", "SPacketUpdateHealth");
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x58, "ScoreboardObjective", "SPacketScoreboardObjective");
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x59, "Mount", "SPacketSetPassengers");
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x5A, "ScoreboardTeam", "SPacketTeams");
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x5B, "ScoreboardScore", "SPacketUpdateScore");
public static final PacketType UPDATE_SIMULATION_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x5C, "SetSimulationDistance");
public static final PacketType SET_SUBTITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x5D, "SetSubtitleText");
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x5E, "UpdateTime", "SPacketTimeUpdate");
public static final PacketType SET_TITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x5F, "SetTitleText");
public static final PacketType SET_TITLES_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x60, "SetTitlesAnimation");
public static final PacketType ENTITY_SOUND = new PacketType(PROTOCOL, SENDER, 0x61, "EntitySound", "SPacketSoundEffect");
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x62, "NamedSoundEffect");
public static final PacketType STOP_SOUND = new PacketType(PROTOCOL, SENDER, 0x63, "StopSound");
public static final PacketType SYSTEM_CHAT = new PacketType(PROTOCOL, SENDER, 0x64, "SystemChat");
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x65, "PlayerListHeaderFooter", "SPacketPlayerListHeaderFooter");
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x66, "NBTQuery");
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x67, "Collect", "SPacketCollectItem");
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x68, "EntityTeleport", "SPacketEntityTeleport");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x69, "Advancements", "SPacketAdvancementInfo");
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x6A, "UpdateAttributes", "SPacketEntityProperties");
public static final PacketType UPDATE_ENABLED_FEATURES = new PacketType(PROTOCOL, SENDER, 0x6B, "UpdateEnabledFeatures");
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x6C, "EntityEffect", "SPacketEntityEffect");
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x6D, "RecipeUpdate");
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x6E, "Tags");
public static final PacketType ANIMATION = new PacketType(PROTOCOL, SENDER, 0x03, "Animation", "SPacketAnimation");
public static final PacketType STATISTIC = new PacketType(PROTOCOL, SENDER, 0x04, "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, "BlockBreakAnimation", "SPacketBlockBreakAnim");
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x07, "TileEntityData", "SPacketUpdateTileEntity");
public static final PacketType BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x08, "BlockAction", "SPacketBlockAction");
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x09, "BlockChange", "SPacketBlockChange");
public static final PacketType BOSS = new PacketType(PROTOCOL, SENDER, 0x0A, "Boss", "SPacketUpdateBossInfo");
public static final PacketType SERVER_DIFFICULTY = new PacketType(PROTOCOL, SENDER, 0x0B, "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, "TabComplete", "SPacketTabComplete");
public static final PacketType COMMANDS = new PacketType(PROTOCOL, SENDER, 0x11, "Commands");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x12, "CloseWindow", "SPacketCloseWindow");
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x13, "WindowItems", "SPacketWindowItems");
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x14, "WindowData", "SPacketWindowProperty");
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x15, "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, "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, "EntityStatus", "SPacketEntityStatus");
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x20, "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, "GameStateChange", "SPacketChangeGameState");
public static final PacketType OPEN_WINDOW_HORSE = new PacketType(PROTOCOL, SENDER, 0x23, "OpenWindowHorse");
public static final PacketType HURT_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x24, "HurtAnimation", "ClientboundHurtAnimationPacket");
public static final PacketType INITIALIZE_BORDER = new PacketType(PROTOCOL, SENDER, 0x25, "InitializeBorder");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x26, "KeepAlive", "SPacketKeepAlive");
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x27, "LevelChunkWithLight", "MapChunk", "SPacketChunkData");
public static final PacketType WORLD_EVENT = new PacketType(PROTOCOL, SENDER, 0x28, "WorldEvent", "SPacketEffect");
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x29, "WorldParticles", "SPacketParticles");
public static final PacketType LIGHT_UPDATE = new PacketType(PROTOCOL, SENDER, 0x2A, "LightUpdate");
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x2B, "Login", "SPacketJoinGame");
public static final PacketType MAP = new PacketType(PROTOCOL, SENDER, 0x2C, "Map", "SPacketMaps");
public static final PacketType OPEN_WINDOW_MERCHANT = new PacketType(PROTOCOL, SENDER, 0x2D, "OpenWindowMerchant");
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x2E, "Entity$RelEntityMove", "Entity$PacketPlayOutRelEntityMove");
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x2F, "Entity$RelEntityMoveLook", "Entity$PacketPlayOutRelEntityMoveLook");
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x30, "Entity$EntityLook", "Entity$PacketPlayOutEntityLook");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x31, "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, "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, "AutoRecipe", "SPacketPlaceGhostRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x38, "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, "LookAt", "SPacketPlayerPosLook");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x40, "Position");
public static final PacketType RECIPES = new PacketType(PROTOCOL, SENDER, 0x41, "Recipes", "SPacketRecipeBook");
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x42, "EntityDestroy", "SPacketDestroyEntities");
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x43, "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, "EntityHeadRotation", "SPacketEntityHeadLook");
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x49, "MultiBlockChange", "SPacketMultiBlockChange");
public static final PacketType SELECT_ADVANCEMENT_TAB = new PacketType(PROTOCOL, SENDER, 0x4A, "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, "Camera", "SPacketCamera");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x53, "HeldItemSlot", "SPacketHeldItemChange");
public static final PacketType VIEW_CENTRE = new PacketType(PROTOCOL, SENDER, 0x54, "ViewCentre");
public static final PacketType VIEW_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x55, "ViewDistance");
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x56, "SpawnPosition", "SPacketSpawnPosition");
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x57, "ScoreboardDisplayObjective", "SPacketDisplayObjective");
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x58, "EntityMetadata", "SPacketEntityMetadata");
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x59, "AttachEntity", "SPacketEntityAttach");
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x5A, "EntityVelocity", "SPacketEntityVelocity");
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x5B, "EntityEquipment", "SPacketEntityEquipment");
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x5C, "Experience", "SPacketSetExperience");
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x5D, "UpdateHealth", "SPacketUpdateHealth");
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x5E, "ScoreboardObjective", "SPacketScoreboardObjective");
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x5F, "Mount", "SPacketSetPassengers");
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x60, "ScoreboardTeam", "SPacketTeams");
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x61, "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, "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, "EntitySound", "SPacketSoundEffect");
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x68, "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, "PlayerListHeaderFooter", "SPacketPlayerListHeaderFooter");
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x6E, "NBTQuery");
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x6F, "Collect", "SPacketCollectItem");
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x70, "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, "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, "EntityEffect", "SPacketEntityEffect");
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x77, "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");
// ---- Removed in 1.9
@ -346,25 +363,43 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* @deprecated Removed in 1.19.3
*/
@Deprecated
public static final PacketType CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 0x0C, "ChatPreview");
public static final PacketType CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 240, "ChatPreview");
/**
* @deprecated Removed in 1.19.3
*/
@Deprecated
public static final PacketType PLAYER_CHAT_HEADER = new PacketType(PROTOCOL, SENDER, 0x32, "PlayerChatHeader");
public static final PacketType PLAYER_CHAT_HEADER = new PacketType(PROTOCOL, SENDER, 239, "PlayerChatHeader");
/**
* @deprecated Removed in 1.19.3
*/
@Deprecated
public static final PacketType SET_DISPLAY_CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 0x4E, "SetDisplayChatPreview");
public static final PacketType SET_DISPLAY_CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 238, "SetDisplayChatPreview");
/**
* @deprecated Removed in 1.19.3
*/
@Deprecated
public static final PacketType CUSTOM_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x16, "CustomSoundEffect", "SPacketCustomSound");
public static final PacketType CUSTOM_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 237, "CustomSoundEffect", "SPacketCustomSound");
/**
* @deprecated Removed in 1.20.2: moved to configuration phase packets
*/
@Deprecated
public static final PacketType UPDATE_ENABLED_FEATURES = new PacketType(PROTOCOL, SENDER, 236, "UpdateEnabledFeatures");
/**
* @deprecated Removed in 1.20.2
*/
@Deprecated
public static final PacketType NAMED_ENTITY_SPAWN = new PacketType(PROTOCOL, SENDER, 235, "NamedEntitySpawn", "SPacketSpawnPlayer");
/**
* @deprecated Removed in 1.20.4: replaced with new packets for removing and sending resource packs
*/
@Deprecated
public static final PacketType RESOURCE_PACK_SEND = new PacketType(PROTOCOL, SENDER, 234, "ResourcePackSend", "SPacketResourcePackSend");
private static final Server INSTANCE = new Server();
@ -391,52 +426,59 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType DIFFICULTY_CHANGE = new PacketType(PROTOCOL, SENDER, 0x02, "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 = new PacketType(PROTOCOL, SENDER, 0x05, "Chat", "CPacketChatMessage");
public static final PacketType CHAT_SESSION_UPDATE = new PacketType(PROTOCOL, SENDER, 0x06, "ChatSessionUpdate");
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x07, "ClientCommand", "CPacketClientStatus");
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x08, "Settings", "CPacketClientSettings");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x09, "TabComplete", "CPacketTabComplete");
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x0A, "EnchantItem", "CPacketEnchantItem");
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x0B, "WindowClick", "CPacketClickWindow");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x0C, "CloseWindow", "CPacketCloseWindow");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x0D, "CustomPayload", "CPacketCustomPayload");
public static final PacketType B_EDIT = new PacketType(PROTOCOL, SENDER, 0x0E, "BEdit");
public static final PacketType ENTITY_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x0F, "EntityNBTQuery");
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x10, "UseEntity", "CPacketUseEntity");
public static final PacketType JIGSAW_GENERATE = new PacketType(PROTOCOL, SENDER, 0x11, "JigsawGenerate");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x12, "KeepAlive", "CPacketKeepAlive");
public static final PacketType DIFFICULTY_LOCK = new PacketType(PROTOCOL, SENDER, 0x13, "DifficultyLock");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x14, "Flying$Position", "Flying$PacketPlayInPosition", "CPacketPlayer$Position");
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x15, "Flying$PositionLook", "Flying$PacketPlayInPositionLook", "CPacketPlayer$PositionRotation");
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x16, "Flying$Look", "Flying$PacketPlayInLook", "CPacketPlayer$Rotation");
public static final PacketType GROUND = new PacketType(PROTOCOL, SENDER, 0x17, "Flying$d");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x18, "VehicleMove", "CPacketVehicleMove");
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x19, "BoatMove", "CPacketSteerBoat");
public static final PacketType PICK_ITEM = new PacketType(PROTOCOL, SENDER, 0x1A, "PickItem");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x1B, "AutoRecipe", "CPacketPlaceRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x1C, "Abilities", "CPacketPlayerAbilities");
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x1D, "BlockDig", "CPacketPlayerDigging");
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x1E, "EntityAction", "CPacketEntityAction");
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x1F, "SteerVehicle", "CPacketInput");
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x20, "Pong");
public static final PacketType RECIPE_SETTINGS = new PacketType(PROTOCOL, SENDER, 0x21, "RecipeSettings");
public static final PacketType RECIPE_DISPLAYED = new PacketType(PROTOCOL, SENDER, 0x22, "RecipeDisplayed", "CPacketRecipeInfo");
public static final PacketType ITEM_NAME = new PacketType(PROTOCOL, SENDER, 0x23, "ItemName");
public static final PacketType RESOURCE_PACK_STATUS = new PacketType(PROTOCOL, SENDER, 0x24, "ResourcePackStatus", "CPacketResourcePackStatus");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x25, "Advancements", "CPacketSeenAdvancements");
public static final PacketType TR_SEL = new PacketType(PROTOCOL, SENDER, 0x26, "TrSel");
public static final PacketType BEACON = new PacketType(PROTOCOL, SENDER, 0x27, "Beacon");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x28, "HeldItemSlot", "CPacketHeldItemChange");
public static final PacketType SET_COMMAND_BLOCK = new PacketType(PROTOCOL, SENDER, 0x29, "SetCommandBlock");
public static final PacketType SET_COMMAND_MINECART = new PacketType(PROTOCOL, SENDER, 0x2A, "SetCommandMinecart");
public static final PacketType SET_CREATIVE_SLOT = new PacketType(PROTOCOL, SENDER, 0x2B, "SetCreativeSlot", "CPacketCreativeInventoryAction");
public static final PacketType SET_JIGSAW = new PacketType(PROTOCOL, SENDER, 0x2C, "SetJigsaw");
public static final PacketType STRUCT = new PacketType(PROTOCOL, SENDER, 0x2D, "Struct");
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x2E, "UpdateSign", "CPacketUpdateSign");
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x2F, "ArmAnimation", "CPacketAnimation");
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x30, "Spectate", "CPacketSpectate");
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x31, "UseItem", "CPacketPlayerTryUseItemOnBlock");
public static final PacketType BLOCK_PLACE = new PacketType(PROTOCOL, SENDER, 0x32, "BlockPlace", "CPacketPlayerTryUseItem");
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, "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, "EnchantItem", "CPacketEnchantItem");
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x0E, "WindowClick", "CPacketClickWindow");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x0F, "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, "BEdit");
public static final PacketType ENTITY_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x15, "EntityNBTQuery");
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x16, "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, "DifficultyLock");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x1A, "Flying$Position", "Flying$PacketPlayInPosition", "CPacketPlayer$Position");
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x1B, "Flying$PositionLook", "Flying$PacketPlayInPositionLook", "CPacketPlayer$PositionRotation");
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x1C, "Flying$Look", "Flying$PacketPlayInLook", "CPacketPlayer$Rotation");
public static final PacketType GROUND = new PacketType(PROTOCOL, SENDER, 0x1D, "Flying$d");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x1E, "VehicleMove", "CPacketVehicleMove");
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x1F, "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, "AutoRecipe", "CPacketPlaceRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x23, "Abilities", "CPacketPlayerAbilities");
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x24, "BlockDig", "CPacketPlayerDigging");
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x25, "EntityAction", "CPacketEntityAction");
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x26, "SteerVehicle", "CPacketInput");
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x27, "Pong");
public static final PacketType RECIPE_SETTINGS = new PacketType(PROTOCOL, SENDER, 0x28, "RecipeSettings");
public static final PacketType RECIPE_DISPLAYED = new PacketType(PROTOCOL, SENDER, 0x29, "RecipeDisplayed", "CPacketRecipeInfo");
public static final PacketType ITEM_NAME = new PacketType(PROTOCOL, SENDER, 0x2A, "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, "Advancements", "CPacketSeenAdvancements");
public static final PacketType TR_SEL = new PacketType(PROTOCOL, SENDER, 0x2D, "TrSel");
public static final PacketType BEACON = new PacketType(PROTOCOL, SENDER, 0x2E, "Beacon");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x2F, "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, "SetCreativeSlot", "CPacketCreativeInventoryAction");
public static final PacketType SET_JIGSAW = new PacketType(PROTOCOL, SENDER, 0x33, "SetJigsaw");
public static final PacketType STRUCT = new PacketType(PROTOCOL, SENDER, 0x34, "Struct");
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x35, "UpdateSign", "CPacketUpdateSign");
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x36, "ArmAnimation", "CPacketAnimation");
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x37, "Spectate", "CPacketSpectate");
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x38, "UseItem", "CPacketPlayerTryUseItemOnBlock");
public static final PacketType BLOCK_PLACE = new PacketType(PROTOCOL, SENDER, 0x39, "BlockPlace", "CPacketPlayerTryUseItem");
/**
* @deprecated Removed in 1.17
@ -454,7 +496,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
* @deprecated Removed in 1.19.3
*/
@Deprecated
public static final PacketType CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 0x06, "ChatPreview");
public static final PacketType CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 253, "ChatPreview");
private static final Client INSTANCE = new Client();
@ -491,7 +533,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
@ForceAsync
public static final PacketType SERVER_INFO = new PacketType(PROTOCOL, SENDER, 0x00, "ServerInfo", "SPacketServerInfo");
@ForceAsync
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x01, "Pong", "SPacketPong");
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x01, "PongResponse", "Pong", "SPacketPong");
/**
* @deprecated Renamed to {@link #SERVER_INFO}
@ -522,7 +564,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType START = new PacketType(PROTOCOL, SENDER, 0x00, "Start", "CPacketServerQuery");
@ForceAsync
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x01, "Ping", "CPacketPing");
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x01, "PingRequest", "Ping", "CPacketPing");
private static final Client INSTANCE = new Client();
@ -562,6 +604,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType SUCCESS = new PacketType(PROTOCOL, SENDER, 0x02, "Success", "SPacketLoginSuccess");
public static final PacketType SET_COMPRESSION = new PacketType(PROTOCOL, SENDER, 0x03, "SetCompression", "SPacketEnableCompression");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x04, "CustomPayload", "SPacketCustomPayload");
public static final PacketType COOKIE_REQUEST = new PacketType(PROTOCOL, SENDER, 0x05, "CookieRequest");
private static final Server INSTANCE = new Server();
@ -585,7 +628,9 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType START = new PacketType(PROTOCOL, SENDER, 0x00, "Start", "CPacketLoginStart");
public static final PacketType ENCRYPTION_BEGIN = new PacketType(PROTOCOL, SENDER, 0x01, "EncryptionBegin", "CPacketEncryptionResponse");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x02, "CustomPayload", "CPacketCustomPayload");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x02, "CustomQueryAnswer", "CustomPayload", "CPacketCustomPayload");
public static final PacketType LOGIN_ACK = new PacketType(PROTOCOL, SENDER, 0x03, "LoginAcknowledged");
public static final PacketType COOKIE_RESPONSE = new PacketType(PROTOCOL, SENDER, 0x04, "CookieResponse");
private static final Client INSTANCE = new Client();
@ -605,6 +650,77 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
}
}
/**
* Packets sent and received during client configuration.
* @author Pasqual Koschmieder
*/
public static class Configuration {
static final Protocol PROTOCOL = Protocol.CONFIGURATION;
/**
* Outgoing packets.
*/
public static class Server extends PacketTypeEnum {
private static final Sender SENDER = Sender.SERVER;
public static final PacketType COOKIE_REQUEST = new PacketType(PROTOCOL, SENDER, 0x00, "CookieRequest");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x01, "CustomPayload");
public static final PacketType DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x02, "Disconnect");
public static final PacketType FINISH_CONFIGURATION = new PacketType(PROTOCOL, SENDER, 0x03, "FinishConfiguration");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x04, "KeepAlive");
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x05, "Ping");
public static final PacketType RESET_CHAT = new PacketType(PROTOCOL, SENDER, 0x06, "ResetChat");
public static final PacketType REGISTRY_DATA = new PacketType(PROTOCOL, SENDER, 0x07, "RegistryData");
public static final PacketType REMOVE_RESOURCE_PACK = new PacketType(PROTOCOL, SENDER, 0x08, "ResourcePackPop");
public static final PacketType ADD_RESOURCE_PACK = new PacketType(PROTOCOL, SENDER, 0x09, "ResourcePackPush");
public static final PacketType STORE_COOKIE = new PacketType(PROTOCOL, SENDER, 0x0A, "StoreCookie");
public static final PacketType TRANSFER = new PacketType(PROTOCOL, SENDER, 0x0B, "Transfer");
public static final PacketType UPDATE_ENABLED_FEATURES = new PacketType(PROTOCOL, SENDER, 0x0C, "UpdateEnabledFeatures");
public static final PacketType UPDATE_TAGS = new PacketType(PROTOCOL, SENDER, 0x0D, "UpdateTags");
public static final PacketType SELECT_KNOWN_PACKS = new PacketType(PROTOCOL, SENDER, 0x0E, "ClientboundSelectKnownPacks");
/**
* @deprecated Removed in 1.20.4: replaced with new packets for removing and sending resource packs
*/
@Deprecated
public static final PacketType RESOURCE_PACK = new PacketType(PROTOCOL, SENDER, 255, "ResourcePack");
private static final Server INSTANCE = new Server();
// Prevent accidental construction
private Server() { super(); }
public static Server getInstance() {
return INSTANCE;
}
}
/**
* Incoming packets.
*/
public static class Client extends PacketTypeEnum {
private static final Sender SENDER = Sender.CLIENT;
public static final PacketType CLIENT_INFORMATION = new PacketType(PROTOCOL, SENDER, 0x00, "ClientInformation");
public static final PacketType COOKIE_RESPONSE = new PacketType(PROTOCOL, SENDER, 0x01, "CookieResponse");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x02, "CustomPayload");
public static final PacketType FINISH_CONFIGURATION = new PacketType(PROTOCOL, SENDER, 0x03, "FinishConfiguration");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x04, "KeepAlive");
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");
private static final Client INSTANCE = new Client();
// Prevent accidental construction
private Client() { super(); }
public static Client getInstance() {
return INSTANCE;
}
}
}
/**
* Represents the different protocol or connection states.
* @author Kristian
@ -614,6 +730,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
PLAY("Play", "game"),
STATUS("Status", "status"),
LOGIN("Login", "login"),
CONFIGURATION("Configuration", "configuration"),
/**
* Only for packets removed in Minecraft 1.7.2
@ -639,6 +756,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
case "PLAY": return PLAY;
case "STATUS": return STATUS;
case "LOGIN": return LOGIN;
case "CONFIGURATION": return CONFIGURATION;
default:
throw new IllegalArgumentException("Unrecognized vanilla enum " + vanilla);
}
@ -731,7 +849,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
private boolean forceAsync;
private boolean dynamic;
private int hashCode;
private transient int hashCode;
/**
* Retrieve the current packet/legacy lookup.
@ -747,7 +865,9 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
addPacketTypes(Status.Client.getInstance()).
addPacketTypes(Status.Server.getInstance()).
addPacketTypes(Login.Client.getInstance()).
addPacketTypes(Login.Server.getInstance());
addPacketTypes(Login.Server.getInstance()).
addPacketTypes(Configuration.Client.getInstance()).
addPacketTypes(Configuration.Server.getInstance());
}
return LOOKUP;
}
@ -854,6 +974,14 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
}
}
private static String formatSimpleClassName(Protocol protocol, Sender sender, String name) {
return "Packet" + protocol.getPacketName() + sender.getPacketName() + name;
}
private static String formatSimpleMojangClassName(Protocol protocol, Sender sender, String name) {
return sender.getMojangName() + name + "Packet";
}
private static String formatMojangClassName(Protocol protocol, Sender sender, String name) {
return "net.minecraft.network.protocol." + protocol.getMojangName() + "." + sender.getMojangName()
+ name + "Packet";
@ -924,7 +1052,8 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
return type;
}
static Consumer<String> onDynamicCreate = x -> {};
static BiConsumer<PacketType, String> onDynamicCreate = (type, className) -> {};
static BiConsumer<PacketType, Integer> onIdMismatch = (type, newId) -> {};
/**
* Retrieve a packet type from a protocol, sender, ID, and class for 1.8+
@ -942,7 +1071,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
// Check the map first
String className = packetClass.getName();
PacketType type = find(map, className);
PacketType type = find(map, packetClass);
if (type == null) {
// Guess we don't support this packet :/
type = new PacketType(protocol, sender, packetId, PROTOCOL_VERSION, className);
@ -950,14 +1079,17 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
// Many may be scheduled, but only the first will be executed
scheduleRegister(type, "Dynamic-" + UUID.randomUUID().toString());
onDynamicCreate.accept(className);
onDynamicCreate.accept(type, className);
} else if (packetId != type.getCurrentId()) {
onIdMismatch.accept(type, packetId);
}
return type;
}
private static PacketType find(Map<String, PacketType> map, String clazz) {
PacketType ret = map.get(clazz);
private static PacketType find(Map<String, PacketType> map, Class<?> packetClass) {
String className = packetClass.getName();
PacketType ret = map.get(className);
if (ret != null) {
return ret;
}
@ -967,7 +1099,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
List<String> aliases = check.getClassNames();
if (aliases.size() > 1) {
for (String alias : aliases) {
if (alias.equals(clazz)) {
if (alias.equals(className) || alias.equals(packetClass.getSimpleName())) {
// We have a match!
return check;
}
@ -981,8 +1113,11 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
/**
* Lookup a packet type from a packet class.
* @param packetClass - the packet class.
* @return The corresponding packet type, or NULL if not found.
* @return The corresponding packet type, never null.
* @throws IllegalArgumentException if the given packet class is not a registered packet.
* @deprecated since 1.20.2 there are packet classes that are shared between protocol states, therefore the result can be invalid.
*/
@Deprecated
public static PacketType fromClass(Class<?> packetClass) {
PacketType type = PacketRegistry.getPacketType(packetClass);
@ -1086,7 +1221,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
this.sender = Preconditions.checkNotNull(sender, "sender cannot be NULL");
this.currentId = currentId;
this.version = version;
this.classNames = new ArrayList<>();
for (String classname : names) {
if (isMcpPacketName(classname)) { // Minecraft MCP packets
@ -1094,6 +1229,9 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
} else {
classNames.add(formatClassName(protocol, sender, classname));
classNames.add(formatMojangClassName(protocol, sender, classname));
classNames.add(formatSimpleClassName(protocol, sender, classname));
classNames.add(formatSimpleMojangClassName(protocol, sender, classname));
classNames.add(classname);
}
}

View File

@ -17,6 +17,7 @@ import com.google.common.collect.Multimap;
* @author Kristian
*/
class PacketTypeLookup {
public static class ProtocolSenderLookup {
// Unroll lookup for performance reasons
public final IntegerMap<PacketType> HANDSHAKE_CLIENT = new IntegerMap<>();
@ -27,6 +28,8 @@ class PacketTypeLookup {
public final IntegerMap<PacketType> STATUS_SERVER = new IntegerMap<>();
public final IntegerMap<PacketType> LOGIN_CLIENT = new IntegerMap<>();
public final IntegerMap<PacketType> LOGIN_SERVER = new IntegerMap<>();
public final IntegerMap<PacketType> CONFIGURATION_CLIENT = new IntegerMap<>();
public final IntegerMap<PacketType> CONFIGURATION_SERVER = new IntegerMap<>();
/**
* Retrieve the correct integer map for a specific protocol and sender.
@ -44,6 +47,8 @@ class PacketTypeLookup {
return sender == Sender.CLIENT ? STATUS_CLIENT : STATUS_SERVER;
case LOGIN:
return sender == Sender.CLIENT ? LOGIN_CLIENT : LOGIN_SERVER;
case CONFIGURATION:
return sender == Sender.CLIENT ? CONFIGURATION_CLIENT : CONFIGURATION_SERVER;
default:
throw new IllegalArgumentException("Unable to find protocol " + protocol);
}
@ -60,7 +65,9 @@ class PacketTypeLookup {
public final Map<String, PacketType> STATUS_SERVER = new ConcurrentHashMap<>();
public final Map<String, PacketType> LOGIN_CLIENT = new ConcurrentHashMap<>();
public final Map<String, PacketType> LOGIN_SERVER = new ConcurrentHashMap<>();
public final Map<String, PacketType> CONFIGURATION_CLIENT = new ConcurrentHashMap<>();
public final Map<String, PacketType> CONFIGURATION_SERVER = new ConcurrentHashMap<>();
/**
* Retrieve the correct integer map for a specific protocol and sender.
* @param protocol - the protocol.
@ -77,17 +84,14 @@ class PacketTypeLookup {
return sender == Sender.CLIENT ? STATUS_CLIENT : STATUS_SERVER;
case LOGIN:
return sender == Sender.CLIENT ? LOGIN_CLIENT : LOGIN_SERVER;
case CONFIGURATION:
return sender == Sender.CLIENT ? CONFIGURATION_CLIENT : CONFIGURATION_SERVER;
default:
throw new IllegalArgumentException("Unable to find protocol " + protocol);
}
}
}
// Packet IDs from 1.6.4 and below
private final IntegerMap<PacketType> legacyLookup = new IntegerMap<>();
private final IntegerMap<PacketType> serverLookup = new IntegerMap<>();
private final IntegerMap<PacketType> clientLookup = new IntegerMap<>();
// Packets for 1.7.2
private final ProtocolSenderLookup idLookup = new ProtocolSenderLookup();
@ -119,9 +123,11 @@ class PacketTypeLookup {
* Retrieve a packet type from a legacy (1.6.4 and below) packet ID.
* @param packetId - the legacy packet ID.
* @return The corresponding packet type, or NULL if not found.
* @deprecated no longer works and will always return null
*/
@Deprecated
public PacketType getFromLegacy(int packetId) {
return legacyLookup.get(packetId);
return null;
}
/**
@ -138,20 +144,11 @@ class PacketTypeLookup {
* @param packetId - the legacy packet ID.
* @param preference - which packet type to look for first.
* @return The corresponding packet type, or NULL if not found.
* @deprecated no longer works and will always return null
*/
public PacketType getFromLegacy(int packetId, Sender preference) {
if (preference == Sender.CLIENT)
return getFirst(packetId, clientLookup, serverLookup);
else
return getFirst(packetId, serverLookup, clientLookup);
}
// Helper method for looking up in two sets
private <T> T getFirst(int packetId, IntegerMap<T> first, IntegerMap<T> second) {
if (first.containsKey(packetId))
return first.get(packetId);
else
return second.get(packetId);
@Deprecated
public PacketType getFromLegacy(int packetId, Sender preference) {
return null;
}
/**

View File

@ -37,12 +37,12 @@ public class ProtocolLibrary {
/**
* The maximum version ProtocolLib has been tested with.
*/
public static final String MAXIMUM_MINECRAFT_VERSION = "1.20.1";
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.20.1) 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 = "2023-06-12";
public static final String MINECRAFT_LAST_RELEASE_DATE = "2023-12-07";
/**
* Plugins that are currently incompatible with ProtocolLib.

View File

@ -413,6 +413,23 @@ public class AsyncFilterManager implements AsynchronousManager {
// Only send if the packet is ready
if (marker.decrementProcessingDelay() == 0) {
// Now, get the next non-cancelled listener
if (!marker.hasExpired()) {
for (; marker.getListenerTraversal().hasNext(); ) {
AsyncListenerHandler handler = marker.getListenerTraversal().next().getListener();
if (!handler.isCancelled()) {
marker.incrementProcessingDelay();
handler.enqueuePacket(packet);
return;
}
}
}
// There are no more listeners - queue the packet for transmission
signalFreeProcessingSlot(packet, onMainThread);
PacketSendingQueue queue = getSendingQueue(packet, false);
// No need to create a new queue if the player has logged out
@ -450,11 +467,18 @@ public class AsyncFilterManager implements AsynchronousManager {
}
/**
* Signal that a packet has finished processing.
* Signal that a packet has finished processing. Tries to process further packets
* if a processing slot is still free.
* @param packet - packet to signal.
* @param onMainThread whether or not this method was run by the main thread.
*/
public void signalFreeProcessingSlot(PacketEvent packet) {
getProcessingQueue(packet).signalProcessingDone();
public void signalFreeProcessingSlot(PacketEvent packet, boolean onMainThread) {
PacketProcessingQueue queue = getProcessingQueue(packet);
// mark slot as done
queue.signalProcessingDone();
// start processing next slot if possible
queue.signalBeginProcessing(onMainThread);
}
/**

View File

@ -24,6 +24,8 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
@ -38,8 +40,6 @@ import com.comphenix.protocol.timing.TimedTracker;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import org.bukkit.plugin.Plugin;
/**
* Represents a handler for an asynchronous event.
* <p>
@ -328,7 +328,7 @@ public class AsyncListenerHandler {
}
private void scheduleAsync(Runnable runnable) {
listener.getPlugin().getServer().getScheduler().runTaskAsynchronously(listener.getPlugin(), runnable);
filterManager.getScheduler().runTaskAsync(runnable);
}
/**
@ -632,21 +632,6 @@ public class AsyncListenerHandler {
filterManager.getErrorReporter().reportMinimal(listener.getPlugin(), methodName, e);
}
// Now, get the next non-cancelled listener
if (!marker.hasExpired()) {
for (; marker.getListenerTraversal().hasNext(); ) {
AsyncListenerHandler handler = marker.getListenerTraversal().next().getListener();
if (!handler.isCancelled()) {
handler.enqueuePacket(packet);
return;
}
}
}
// There are no more listeners - queue the packet for transmission
filterManager.signalFreeProcessingSlot(packet);
// Note that listeners can opt to delay the packet transmission
filterManager.signalPacketTransmission(packet);
}

View File

@ -1,10 +1,5 @@
package com.comphenix.protocol.events;
import javax.annotation.Nonnull;
import java.lang.reflect.Array;
import java.time.Instant;
import java.util.*;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
@ -17,7 +12,6 @@ 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;
@ -31,6 +25,11 @@ import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
import java.lang.reflect.Array;
import java.time.Instant;
import java.util.*;
public abstract class AbstractStructure {
protected transient Object handle;
protected transient StructureModifier<Object> structureModifier;
@ -410,7 +409,7 @@ public abstract class AbstractStructure {
}
/**
* Retrieves a read/write structure for collections of watchable objects.
* Retrieves a read/write structure for collections of watchable objects before Minecraft 1.19.3.
* <p>
* This modifier will automatically marshal between the visible WrappedWatchableObject and the
* internal Minecraft WatchableObject.
@ -424,7 +423,7 @@ public abstract class AbstractStructure {
}
/**
* Retrieves a read/write structure for collections of data values.
* Retrieves a read/write structure for collections of data values for Minecraft 1.19.3 or later.
* @return A modifier for data values.
*/
public StructureModifier<List<WrappedDataValue>> getDataValueCollectionModifier() {
@ -868,6 +867,16 @@ public abstract class AbstractStructure {
MinecraftKey.getConverter());
}
/**
* Retrieve a read/write structure for custom packet payloads (available since Minecraft 1.20.2).
* @return A modifier for CustomPacketPayloads fields.
*/
public StructureModifier<CustomPacketPayloadWrapper> getCustomPacketPayloads() {
return structureModifier.withType(
CustomPacketPayloadWrapper.getCustomPacketPayloadClass(),
CustomPacketPayloadWrapper.getConverter());
}
/**
* Retrieve a read/write structure for dimension IDs in 1.13.1+
* @return A modifier for dimension IDs

View File

@ -34,6 +34,7 @@ import java.util.function.Function;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.injector.StructureCache;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.ObjectWriter;
import com.comphenix.protocol.reflect.StructureModifier;
@ -56,6 +57,7 @@ import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.Converters;
import com.comphenix.protocol.wrappers.WrappedStreamCodec;
import com.google.common.collect.Sets;
import io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCountUtil;
@ -345,6 +347,11 @@ public class PacketContainer extends AbstractStructure implements Serializable {
}
Function<Object, Object> deserializer = PACKET_DESERIALIZER_METHODS.computeIfAbsent(packetType, type -> {
WrappedStreamCodec streamCodec = PacketRegistry.getStreamCodec(type.getPacketClass());
if (streamCodec != null) {
return streamCodec::decode;
}
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
// best guess - a constructor which takes a buffer as the only argument
ConstructorAccessor bufferConstructor = Accessors.getConstructorAccessorOrNull(
@ -392,7 +399,14 @@ public class PacketContainer extends AbstractStructure implements Serializable {
}
Object targetBuffer = MinecraftReflection.createPacketDataSerializer(0);
MinecraftMethods.getPacketWriteByteBufMethod().invoke(handle, targetBuffer);
WrappedStreamCodec streamCodec = PacketRegistry.getStreamCodec(type.getPacketClass());
if (streamCodec != null) {
streamCodec.encode(targetBuffer, handle);
} else {
MinecraftMethods.getPacketWriteByteBufMethod().invoke(handle, targetBuffer);
}
return targetBuffer;
}

View File

@ -40,6 +40,7 @@ import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.profile.PlayerProfile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.ObjectInputStream;
@ -48,6 +49,9 @@ import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@ -141,6 +145,16 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
return lastSeen;
}
@Override
public Location getRespawnLocation() {
return null;
}
@Override
public Location getLocation() {
return null;
}
// TODO do we need to implement this?
public void incrementStatistic(Statistic statistic) throws IllegalArgumentException {
@ -240,6 +254,24 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
return banned;
}
@Nullable
public BanEntry<PlayerProfile> ban(@Nullable String s, @Nullable Date date, @Nullable String s1) {
setBanned(true);
return null;
}
@Nullable
public BanEntry<PlayerProfile> ban(@Nullable String s, @Nullable Instant instant, @Nullable String s1) {
setBanned(true);
return null;
}
@Nullable
public BanEntry<PlayerProfile> ban(@Nullable String s, @Nullable Duration duration, @Nullable String s1) {
setBanned(true);
return null;
}
public void setBanned(boolean banned) {
this.banned = banned;
}

View File

@ -46,6 +46,8 @@ public interface ListenerInvoker {
*
* @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);
}

View File

@ -20,22 +20,28 @@ package com.comphenix.protocol.injector;
import java.security.PublicKey;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.injector.packet.PacketRegistry;
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.ConstructorAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
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 com.comphenix.protocol.utility.ZeroBuffer;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedStreamCodec;
import com.google.common.base.Preconditions;
import io.netty.buffer.ByteBuf;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy.Default;
@ -57,22 +63,47 @@ public class StructureCache {
private static final Object TRICK_INIT_LOCK = new Object();
private static boolean TRICK_TRIED = false;
private static ConstructorAccessor TRICKED_DATA_SERIALIZER_BASE;
private static ConstructorAccessor TRICKED_DATA_SERIALIZER_JSON;
private static Supplier<Object> TRICKED_DATA_SERIALIZER_BASE;
private static Supplier<Object> TRICKED_DATA_SERIALIZER_JSON;
public static Object newPacket(Class<?> packetClass) {
Supplier<Object> packetConstructor = PACKET_INSTANCE_CREATORS.computeIfAbsent(packetClass, clazz -> {
Supplier<Object> packetConstructor = PACKET_INSTANCE_CREATORS.computeIfAbsent(packetClass, packetClassKey -> {
WrappedStreamCodec streamCodec = PacketRegistry.getStreamCodec(packetClassKey);
// use the new stream codec for versions above 1.20.5 if possible
if (streamCodec != null && tryInitTrickDataSerializer()) {
try {
// first try with the base accessor
Object serializer = TRICKED_DATA_SERIALIZER_BASE.get();
streamCodec.decode(serializer); // throwaway instance, for testing
// method is working
return () -> streamCodec.decode(serializer);
} catch (Exception exception) {
try {
// try with the json accessor
Object serializer = TRICKED_DATA_SERIALIZER_JSON.get();
streamCodec.decode(serializer); // throwaway instance, for testing
// method is working
return () -> streamCodec.decode(serializer);
} catch (Exception ignored) {
// shrug, fall back to default behaviour
}
}
}
// prefer construction via PacketDataSerializer constructor on 1.17 and above
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
ConstructorAccessor serializerAccessor = Accessors.getConstructorAccessorOrNull(
clazz,
packetClassKey,
MinecraftReflection.getPacketDataSerializerClass());
if (serializerAccessor != null) {
// check if the method is possible
if (tryInitTrickDataSerializer()) {
try {
// first try with the base accessor
Object serializer = TRICKED_DATA_SERIALIZER_BASE.invoke(new ZeroBuffer());
Object serializer = TRICKED_DATA_SERIALIZER_BASE.get();
serializerAccessor.invoke(serializer); // throwaway instance, for testing
// method is working
@ -80,7 +111,7 @@ public class StructureCache {
} catch (Exception exception) {
try {
// try with the json accessor
Object serializer = TRICKED_DATA_SERIALIZER_JSON.invoke(new ZeroBuffer());
Object serializer = TRICKED_DATA_SERIALIZER_JSON.get();
serializerAccessor.invoke(serializer); // throwaway instance, for testing
// method is working
@ -95,8 +126,8 @@ public class StructureCache {
// try via DefaultInstances as fallback
return () -> {
Object packetInstance = DefaultInstances.DEFAULT.create(clazz);
Objects.requireNonNull(packetInstance, "Unable to create packet instance for class " + clazz);
Object packetInstance = DefaultInstances.DEFAULT.create(packetClassKey);
Objects.requireNonNull(packetInstance, "Unable to create packet instance for class " + packetClassKey + " - " + tryInitTrickDataSerializer() + " - " + streamCodec);
return packetInstance;
};
});
@ -118,7 +149,9 @@ public class StructureCache {
*
* @param packetType - packet type.
* @return A structure modifier.
* @deprecated use {@link #getStructure(PacketType)} instead.
*/
@Deprecated
public static StructureModifier<Object> getStructure(Class<?> packetType) {
// Get the ID from the class
PacketType type = PacketRegistry.getPacketType(packetType);
@ -154,9 +187,63 @@ public class StructureCache {
*/
public static Object newNullDataSerializer() {
tryInitTrickDataSerializer();
return TRICKED_DATA_SERIALIZER_BASE.invoke(new ZeroBuffer());
return TRICKED_DATA_SERIALIZER_BASE.get();
}
static void initTrickDataSerializer() {
Optional<Class<?>> registryByteBuf = MinecraftReflection.getRegistryFriendlyByteBufClass();
// create an empty instance of a nbt tag compound / text compound that we can re-use when needed
Object textCompound = WrappedChatComponent.fromText("").getHandle();
Object compound = Accessors.getConstructorAccessor(MinecraftReflection.getNBTCompoundClass()).invoke();
// base builder which intercepts a few methods
DynamicType.Builder<?> baseBuilder = ByteBuddyFactory.getInstance()
.createSubclass(registryByteBuf.orElse(MinecraftReflection.getPacketDataSerializerClass()))
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerBase")
.method(ElementMatchers.takesArguments(MinecraftReflection.getNBTReadLimiterClass())
.and(ElementMatchers.returns(ElementMatchers.isSubTypeOf(MinecraftReflection.getNBTBaseClass()))))
.intercept(FixedValue.value(compound))
.method(ElementMatchers.returns(MinecraftReflection.getIChatBaseComponentClass()))
.intercept(FixedValue.value(textCompound))
.method(ElementMatchers.returns(PublicKey.class).and(ElementMatchers.takesNoArguments()))
.intercept(FixedValue.nullValue());
Class<?> serializerBase = baseBuilder.make()
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
.getLoaded();
if (registryByteBuf.isPresent()) {
ConstructorAccessor accessor = Accessors.getConstructorAccessor(FuzzyReflection.fromClass(serializerBase, true).getConstructor(FuzzyMethodContract.newBuilder()
.parameterDerivedOf(ByteBuf.class)
.parameterDerivedOf(MinecraftReflection.getRegistryAccessClass())
.build()));
TRICKED_DATA_SERIALIZER_BASE = () -> accessor.invoke(new ZeroBuffer(), MinecraftRegistryAccess.get());
} else {
ConstructorAccessor accessor = Accessors.getConstructorAccessor(serializerBase, ByteBuf.class);
TRICKED_DATA_SERIALIZER_BASE = () -> accessor.invoke(new ZeroBuffer());
}
//xtended builder which intercepts the read string method as well
Class<?> withStringIntercept = baseBuilder
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerJson")
.method(ElementMatchers.returns(String.class).and(ElementMatchers.takesArguments(int.class)))
.intercept(FixedValue.value("{}"))
.make()
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
.getLoaded();
if (registryByteBuf.isPresent()) {
ConstructorAccessor accessor = Accessors.getConstructorAccessor(FuzzyReflection.fromClass(withStringIntercept).getConstructor(FuzzyMethodContract.newBuilder()
.parameterDerivedOf(ByteBuf.class)
.parameterDerivedOf(MinecraftReflection.getRegistryAccessClass())
.build()));
TRICKED_DATA_SERIALIZER_JSON = () -> accessor.invoke(new ZeroBuffer(), MinecraftRegistryAccess.get());
} else {
ConstructorAccessor accessor = Accessors.getConstructorAccessor(withStringIntercept, ByteBuf.class);
TRICKED_DATA_SERIALIZER_JSON = () -> accessor.invoke(new ZeroBuffer());
}
}
/**
* Creates a packet data serializer sub-class if needed to allow the fixed read of a NbtTagCompound because of a
* null check in the MapChunk packet constructor.
@ -172,43 +259,13 @@ public class StructureCache {
}
try {
// create an empty instance of a nbt tag compound / text compound that we can re-use when needed
Object textCompound = WrappedChatComponent.fromText("").getHandle();
Object compound = Accessors.getConstructorAccessor(MinecraftReflection.getNBTCompoundClass()).invoke();
// base builder which intercepts a few methods
DynamicType.Builder<?> baseBuilder = ByteBuddyFactory.getInstance()
.createSubclass(MinecraftReflection.getPacketDataSerializerClass())
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerBase")
.method(ElementMatchers.returns(MinecraftReflection.getNBTCompoundClass())
.and(ElementMatchers.takesArguments(MinecraftReflection.getNBTReadLimiterClass())))
.intercept(FixedValue.value(compound))
.method(ElementMatchers.returns(MinecraftReflection.getIChatBaseComponentClass()))
.intercept(FixedValue.value(textCompound))
.method(ElementMatchers.returns(PublicKey.class).and(ElementMatchers.takesNoArguments()))
.intercept(FixedValue.nullValue());
Class<?> serializerBase = baseBuilder.make()
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
.getLoaded();
TRICKED_DATA_SERIALIZER_BASE = Accessors.getConstructorAccessor(serializerBase, ByteBuf.class);
// extended builder which intercepts the read string method as well
Class<?> withStringIntercept = baseBuilder
.name(MinecraftMethods.class.getPackage().getName() + ".ProtocolLibTricksNmsDataSerializerJson")
.method(ElementMatchers.returns(String.class).and(ElementMatchers.takesArguments(int.class)))
.intercept(FixedValue.value("{}"))
.make()
.load(ByteBuddyFactory.getInstance().getClassLoader(), Default.INJECTION)
.getLoaded();
TRICKED_DATA_SERIALIZER_JSON = Accessors.getConstructorAccessor(withStringIntercept, ByteBuf.class);
// worked
initTrickDataSerializer();
return true;
} catch (Exception ignored) {
} finally {
TRICK_TRIED = true;
}
// didn't work
return false;
}
}

View File

@ -1,5 +1,6 @@
package com.comphenix.protocol.injector.netty;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.events.NetworkMarker;
import org.bukkit.entity.Player;
@ -49,8 +50,21 @@ public interface Injector {
* Retrieve the current protocol state.
*
* @return The current protocol.
* @deprecated use {@link #getCurrentProtocol(PacketType.Sender)} instead.
*/
Protocol getCurrentProtocol();
@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
* protocol states.
*
* @param sender the side for which the state should be resolved.
* @return The current protocol.
*/
Protocol getCurrentProtocol(PacketType.Sender sender);
/**
* Retrieve the network marker associated with a given packet.

View File

@ -0,0 +1,206 @@
package com.comphenix.protocol.injector.netty.channel;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import com.comphenix.protocol.PacketType;
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.utility.MinecraftReflection;
import io.netty.channel.Channel;
import io.netty.util.AttributeKey;
@SuppressWarnings("unchecked")
final class ChannelProtocolUtil {
public static final BiFunction<Channel, PacketType.Sender, PacketType.Protocol> PROTOCOL_RESOLVER;
static {
Class<?> networkManagerClass = MinecraftReflection.getNetworkManagerClass();
List<Field> attributeKeys = FuzzyReflection.fromClass(networkManagerClass, true).getFieldList(FuzzyFieldContract.newBuilder()
.typeExact(AttributeKey.class)
.requireModifier(Modifier.STATIC)
.declaringClassExactType(networkManagerClass)
.build());
BiFunction<Channel, PacketType.Sender, Object> baseResolver = null;
if (attributeKeys.isEmpty()) {
// since 1.20.5 the protocol is stored as final field in de-/encoder
baseResolver = new Post1_20_5WrappedResolver();
} else if (attributeKeys.size() == 1) {
// if there is only one attribute key we can assume it's the correct one (1.8 - 1.20.1)
Object protocolKey = Accessors.getFieldAccessor(attributeKeys.get(0)).get(null);
baseResolver = new Pre1_20_2DirectResolver((AttributeKey<Object>) protocolKey);
} else if (attributeKeys.size() > 1) {
// most likely 1.20.2+: 1 protocol key per protocol direction
AttributeKey<Object> serverBoundKey = null;
AttributeKey<Object> clientBoundKey = null;
for (Field keyField : attributeKeys) {
AttributeKey<Object> key = (AttributeKey<Object>) Accessors.getFieldAccessor(keyField).get(null);
if (key.name().equals("protocol")) {
// legacy (pre 1.20.2 name) - fall back to the old behaviour
baseResolver = new Pre1_20_2DirectResolver(key);
break;
}
if (key.name().contains("protocol")) {
// one of the two protocol keys for 1.20.2
if (key.name().contains("server")) {
serverBoundKey = key;
} else {
clientBoundKey = key;
}
}
}
if (baseResolver == null) {
if ((serverBoundKey == null || clientBoundKey == null)) {
// neither pre 1.20.2 key nor 1.20.2+ keys are available
throw new ExceptionInInitializerError("Unable to resolve protocol state attribute keys");
} else {
baseResolver = new Post1_20_2WrappedResolver(serverBoundKey, clientBoundKey);
}
}
} else {
throw new ExceptionInInitializerError("Unable to resolve protocol state attribute key(s)");
}
// decorate the base resolver by wrapping its return value into our packet type value
PROTOCOL_RESOLVER = baseResolver.andThen(nmsProtocol -> PacketType.Protocol.fromVanilla((Enum<?>) nmsProtocol));
}
private static final class Pre1_20_2DirectResolver implements BiFunction<Channel, PacketType.Sender, Object> {
private final AttributeKey<Object> attributeKey;
public Pre1_20_2DirectResolver(AttributeKey<Object> attributeKey) {
this.attributeKey = attributeKey;
}
@Override
public Object apply(Channel channel, PacketType.Sender sender) {
return channel.attr(this.attributeKey).get();
}
}
private static final class Post1_20_2WrappedResolver implements BiFunction<Channel, PacketType.Sender, Object> {
private final AttributeKey<Object> serverBoundKey;
private final AttributeKey<Object> clientBoundKey;
// lazy initialized when needed
private FieldAccessor protocolAccessor;
public Post1_20_2WrappedResolver(AttributeKey<Object> serverBoundKey, AttributeKey<Object> clientBoundKey) {
this.serverBoundKey = serverBoundKey;
this.clientBoundKey = clientBoundKey;
}
@Override
public Object apply(Channel channel, PacketType.Sender sender) {
AttributeKey<Object> key = this.getKeyForSender(sender);
Object codecData = channel.attr(key).get();
if (codecData == null) {
return null;
}
FieldAccessor protocolAccessor = this.getProtocolAccessor(codecData.getClass());
return protocolAccessor.get(codecData);
}
private AttributeKey<Object> getKeyForSender(PacketType.Sender sender) {
switch (sender) {
case SERVER:
return this.clientBoundKey;
case CLIENT:
return this.serverBoundKey;
default:
throw new IllegalArgumentException("Illegal packet sender " + sender.name());
}
}
private FieldAccessor getProtocolAccessor(Class<?> codecClass) {
if (this.protocolAccessor == null) {
Class<?> enumProtocolClass = MinecraftReflection.getEnumProtocolClass();
this.protocolAccessor = Accessors.getFieldAccessor(codecClass, enumProtocolClass, true);
}
return this.protocolAccessor;
}
}
/**
* Since 1.20.5 the protocol is stored as final field in de-/encoder
*/
private static final class Post1_20_5WrappedResolver implements BiFunction<Channel, PacketType.Sender, Object> {
// lazy initialized when needed
private Function<Object, Object> serverProtocolAccessor;
private Function<Object, Object> clientProtocolAccessor;
@Override
public Object apply(Channel channel, PacketType.Sender sender) {
String key = this.getKeyForSender(sender);
Object codecHandler = channel.pipeline().get(key);
if (codecHandler == null) {
return null;
}
Function<Object, Object> protocolAccessor = this.getProtocolAccessor(codecHandler.getClass(), sender);
return protocolAccessor.apply(codecHandler);
}
private Function<Object, Object> getProtocolAccessor(Class<?> codecHandler, PacketType.Sender sender) {
switch (sender) {
case SERVER:
if (this.serverProtocolAccessor == null) {
this.serverProtocolAccessor = getProtocolAccessor(codecHandler);
}
return this.serverProtocolAccessor;
case CLIENT:
if (this.clientProtocolAccessor == null) {
this.clientProtocolAccessor = getProtocolAccessor(codecHandler);
}
return this.clientProtocolAccessor;
default:
throw new IllegalArgumentException("Illegal packet sender " + sender.name());
}
}
private String getKeyForSender(PacketType.Sender sender) {
switch (sender) {
case SERVER:
return "encoder";
case CLIENT:
return "decoder";
default:
throw new IllegalArgumentException("Illegal packet sender " + sender.name());
}
}
private Function<Object, Object> getProtocolAccessor(Class<?> codecHandler) {
Class<?> protocolInfoClass = MinecraftReflection.getProtocolInfoClass();
MethodAccessor protocolAccessor = Accessors.getMethodAccessor(FuzzyReflection
.fromClass(protocolInfoClass)
.getMethodByReturnTypeAndParameters("id", MinecraftReflection.getEnumProtocolClass(), new Class[0]));
FieldAccessor protocolInfoAccessor = Accessors.getFieldAccessor(codecHandler, protocolInfoClass, true);
// get ProtocolInfo from handler and get EnumProtocol of ProtocolInfo
return (handler) -> {
Object protocolInfo = protocolInfoAccessor.get(handler);
return protocolAccessor.invoke(protocolInfo);
};
}
}
}

View File

@ -1,5 +1,6 @@
package com.comphenix.protocol.injector.netty.channel;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.injector.netty.Injector;
@ -42,7 +43,7 @@ final class EmptyInjector implements Injector {
}
@Override
public Protocol getCurrentProtocol() {
public Protocol getCurrentProtocol(PacketType.Sender sender) {
return Protocol.HANDSHAKING;
}

View File

@ -2,8 +2,6 @@ package com.comphenix.protocol.injector.netty.channel;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
@ -33,6 +31,7 @@ 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;
@ -110,7 +109,6 @@ public class NettyChannelInjector implements Injector {
// lazy initialized fields, if we don't need them we don't bother about them
private Object playerConnection;
private FieldAccessor protocolAccessor;
public NettyChannelInjector(
Player player,
@ -205,9 +203,18 @@ public class NettyChannelInjector implements Injector {
return false;
}
ChannelPipeline pipeline = this.wrappedChannel.pipeline();
// since 1.20.5 the encoder is renamed to outbound_config only in the handshake phase
String encoderName = pipeline.get("outbound_config") != null
? "outbound_config" : "encoder";
// inject our handlers
this.wrappedChannel.pipeline().addAfter("encoder", WIRE_PACKET_ENCODER_NAME, WIRE_PACKET_ENCODER);
this.wrappedChannel.pipeline().addAfter(
pipeline.addAfter(
encoderName,
WIRE_PACKET_ENCODER_NAME,
WIRE_PACKET_ENCODER);
pipeline.addAfter(
"decoder",
INTERCEPTOR_NAME,
new InboundPacketInterceptor(this, this.channelListener));
@ -322,17 +329,8 @@ public class NettyChannelInjector implements Injector {
}
@Override
public Protocol getCurrentProtocol() {
// ensure that the accessor to the protocol field is available
if (this.protocolAccessor == null) {
this.protocolAccessor = Accessors.getFieldAccessor(
this.networkManager.getClass(),
MinecraftReflection.getEnumProtocolClass(),
true);
}
Object nmsProtocol = this.protocolAccessor.get(this.networkManager);
return Protocol.fromVanilla((Enum<?>) nmsProtocol);
public Protocol getCurrentProtocol(PacketType.Sender sender) {
return ChannelProtocolUtil.PROTOCOL_RESOLVER.apply(this.wrappedChannel, sender);
}
@Override

View File

@ -28,7 +28,6 @@ import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.Util;
import com.comphenix.protocol.wrappers.Pair;
import io.netty.channel.ChannelFuture;
import org.bukkit.Server;
@ -93,7 +92,8 @@ public class NetworkManagerInjector implements ChannelListener {
Class<?> packetClass = packet.getClass();
if (marker != null || MinecraftReflection.isBundlePacket(packetClass) || outboundListeners.contains(packetClass)) {
// wrap packet and construct the event
PacketContainer container = new PacketContainer(PacketRegistry.getPacketType(packetClass), packet);
PacketType.Protocol currentProtocol = injector.getCurrentProtocol(PacketType.Sender.SERVER);
PacketContainer container = new PacketContainer(PacketRegistry.getPacketType(currentProtocol, packetClass), packet);
PacketEvent packetEvent = PacketEvent.fromServer(this, container, marker, injector.getPlayer());
// post to all listeners, then return the packet event we constructed
@ -111,7 +111,16 @@ public class NetworkManagerInjector implements ChannelListener {
Class<?> packetClass = packet.getClass();
if (marker != null || inboundListeners.contains(packetClass)) {
// wrap the packet and construct the event
PacketContainer container = new PacketContainer(PacketRegistry.getPacketType(packetClass), packet);
PacketType.Protocol currentProtocol = injector.getCurrentProtocol(PacketType.Sender.CLIENT);
PacketType packetType = PacketRegistry.getPacketType(currentProtocol, packetClass);
// if packet type could not be found, fallback to HANDSHAKING protocol
// temporary workaround for https://github.com/dmulloy2/ProtocolLib/issues/2601
if (packetType == null) {
packetType = PacketRegistry.getPacketType(PacketType.Protocol.HANDSHAKING, packetClass);
}
PacketContainer container = new PacketContainer(packetType, packet);
PacketEvent packetEvent = PacketEvent.fromClient(this, container, marker, injector.getPlayer());
// post to all listeners, then return the packet event we constructed
@ -238,7 +247,6 @@ public class NetworkManagerInjector implements ChannelListener {
// just reset to the list we wrapped originally
ListeningList ourList = (ListeningList) currentFieldValue;
List<Object> original = ourList.getOriginal();
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (original) {
// revert the injection from all values of the list
ourList.unProcessAll();

View File

@ -17,11 +17,6 @@
package com.comphenix.protocol.reflect;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.injector.StructureCache;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.StreamSerializer;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
@ -46,18 +41,6 @@ public class ObjectWriter {
* @return A structure modifier for the given type.
*/
private StructureModifier<Object> getModifier(Class<?> type) {
Class<?> packetClass = MinecraftReflection.getPacketClass();
// Handle subclasses of the packet class with our custom structure cache, if possible
if (!type.equals(packetClass) && packetClass.isAssignableFrom(type)) {
// might be a packet, but some packets are not registered (for example PacketPlayInFlying, only the subtypes are present)
PacketType packetType = PacketRegistry.getPacketType(type);
if (packetType != null) {
// packet is present, delegate to the cache
return StructureCache.getStructure(packetType);
}
}
// Create the structure modifier if we haven't already
StructureModifier<Object> modifier = CACHE.get(type);
if (modifier == null) {

View File

@ -143,8 +143,7 @@ public class StructureModifier<T> {
for (FieldAccessor accessor : fields) {
Field field = accessor.getField();
if (!field.getType().isPrimitive() && !Modifier.isFinal(field.getModifiers())) {
Object defaultInstance = DEFAULT_GENERATOR.getDefault(field.getType());
if (defaultInstance != null) {
if (DEFAULT_GENERATOR.hasDefault(field.getType())) {
requireDefaults.put(accessor, currentFieldIndex);
}
}

View File

@ -26,6 +26,8 @@ import javax.annotation.Nullable;
import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
/**
@ -164,7 +166,19 @@ public class DefaultInstances implements InstanceProvider {
public <T> T getDefault(Class<T> type) {
return getDefaultInternal(type, registered, 0);
}
private final ThreadLocal<Map<Class<?>, Boolean>> cache = ThreadLocal.withInitial(WeakHashMap::new);
/**
* Determines if a given class has a default value.
*
* @param type - the class to check
* @return true if the class has a default value, false otherwise
*/
public boolean hasDefault(Class<?> type) {
return cache.get().computeIfAbsent(type, aClass -> getDefaultInternal(aClass, registered, 0) != null);
}
/**
* Retrieve the constructor with the fewest number of parameters.
* @param <T> Type

View File

@ -29,4 +29,10 @@ public class DefaultScheduler implements ProtocolScheduler {
int taskId = scheduler.scheduleSyncDelayedTask(plugin, task, delay);
return taskId >= 0 ? new DefaultTask(scheduler, taskId) : null;
}
@Override
public Task runTaskAsync(Runnable task) {
int taskId = scheduler.runTaskAsynchronously(plugin, task).getTaskId();
return taskId >= 0 ? new DefaultTask(scheduler, taskId) : null;
}
}

View File

@ -1,7 +1,6 @@
package com.comphenix.protocol.scheduler;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
public class DefaultTask implements Task {
private final int taskId;

View File

@ -9,23 +9,32 @@ import org.bukkit.plugin.Plugin;
import java.util.function.Consumer;
public class FoliaScheduler implements ProtocolScheduler {
private final Object foliaScheduler;
private final Object foliaRegionScheduler;
private final MethodAccessor runAtFixedRate;
private final MethodAccessor runDelayed;
private final MethodAccessor execute;
private final MethodAccessor cancel;
private final Object foliaAsyncScheduler;
private final MethodAccessor executeAsync;
private final Plugin plugin;
public FoliaScheduler(Plugin plugin) {
this.plugin = plugin;
MethodAccessor getScheduler = Accessors.getMethodAccessor(Bukkit.getServer().getClass(), "getGlobalRegionScheduler");
this.foliaScheduler = getScheduler.invoke(Bukkit.getServer());
this.foliaRegionScheduler = getScheduler.invoke(Bukkit.getServer());
this.runAtFixedRate = Accessors.getMethodAccessor(foliaScheduler.getClass(), "runAtFixedRate", Plugin.class,
this.runAtFixedRate = Accessors.getMethodAccessor(foliaRegionScheduler.getClass(), "runAtFixedRate", Plugin.class,
Consumer.class, long.class, long.class);
this.execute = Accessors.getMethodAccessor(foliaScheduler.getClass(), "run", Plugin.class, Consumer.class);
this.runDelayed = Accessors.getMethodAccessor(foliaScheduler.getClass(), "runDelayed", Plugin.class, Consumer.class, long.class);
this.execute = Accessors.getMethodAccessor(foliaRegionScheduler.getClass(), "run", Plugin.class, Consumer.class);
this.runDelayed = Accessors.getMethodAccessor(foliaRegionScheduler.getClass(), "runDelayed", Plugin.class, Consumer.class, long.class);
MethodAccessor getAsyncScheduler = Accessors.getMethodAccessor(Bukkit.getServer().getClass(), "getAsyncScheduler");
foliaAsyncScheduler = getAsyncScheduler.invoke(Bukkit.getServer());
this.executeAsync = Accessors.getMethodAccessor(foliaAsyncScheduler.getClass(), "runNow", Plugin.class, Consumer.class);
Class<?> taskClass = MinecraftReflection.getLibraryClass("io.papermc.paper.threadedregions.scheduler.ScheduledTask");
this.cancel = Accessors.getMethodAccessor(taskClass, "cancel");
@ -33,19 +42,25 @@ public class FoliaScheduler implements ProtocolScheduler {
@Override
public Task scheduleSyncRepeatingTask(Runnable task, long delay, long period) {
Object taskHandle = runAtFixedRate.invoke(foliaScheduler, plugin, (Consumer<Object>)(t -> task.run()), delay, period);
Object taskHandle = runAtFixedRate.invoke(foliaRegionScheduler, plugin, (Consumer<Object>)(t -> task.run()), delay, period);
return new FoliaTask(cancel, taskHandle);
}
@Override
public Task runTask(Runnable task) {
Object taskHandle = execute.invoke(foliaScheduler, plugin, (Consumer<Object>)(t -> task.run()));
Object taskHandle = execute.invoke(foliaRegionScheduler, plugin, (Consumer<Object>)(t -> task.run()));
return new FoliaTask(cancel, taskHandle);
}
@Override
public Task scheduleSyncDelayedTask(Runnable task, long delay) {
Object taskHandle = runDelayed.invoke(foliaScheduler, plugin, (Consumer<Object>)(t -> task.run()), delay);
Object taskHandle = runDelayed.invoke(foliaRegionScheduler, plugin, (Consumer<Object>)(t -> task.run()), delay);
return new FoliaTask(cancel, taskHandle);
}
@Override
public Task runTaskAsync(Runnable task) {
Object taskHandle = executeAsync.invoke(foliaAsyncScheduler, plugin, (Consumer<Object>)(t -> task.run()));
return new FoliaTask(cancel, taskHandle);
}
}

View File

@ -1,12 +1,11 @@
package com.comphenix.protocol.scheduler;
import com.comphenix.protocol.ProtocolLib;
import org.bukkit.plugin.Plugin;
public interface ProtocolScheduler {
Task scheduleSyncRepeatingTask(Runnable task, long delay, long period);
Task runTask(Runnable task);
Task scheduleSyncDelayedTask(Runnable task, long delay);
Task runTaskAsync(Runnable task);
}

View File

@ -19,7 +19,6 @@ package com.comphenix.protocol.updater;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.utility.Closer;
import com.comphenix.protocol.utility.SchedulerUtil;
import org.bukkit.plugin.Plugin;
import java.io.BufferedReader;

View File

@ -62,7 +62,7 @@ public final class ChatExtensions {
// since 1.19 system chat is extracted into a separate packet
packet = new PacketContainer(PacketType.Play.Server.SYSTEM_CHAT);
packet.getStrings().write(0, component.getJson());
packet.getChatComponents().write(0, component);
packet.getBooleans().write(0, false);
} else {
packet = new PacketContainer(PacketType.Play.Server.CHAT);

View File

@ -1,6 +1,7 @@
package com.comphenix.protocol.utility;
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 org.bukkit.entity.Player;
@ -33,7 +34,13 @@ public final class MinecraftFields {
if (NETWORK_ACCESSOR == null) {
Class<?> networkClass = MinecraftReflection.getNetworkManagerClass();
Class<?> connectionClass = MinecraftReflection.getPlayerConnectionClass();
NETWORK_ACCESSOR = Accessors.getFieldAccessor(connectionClass, networkClass, true);
NETWORK_ACCESSOR = FuzzyReflection.fromClass(connectionClass, true)
.getDeclaredFields(Object.class)
.stream()
.filter(field -> field.getType().equals(networkClass))
.findFirst()
.map(Accessors::getFieldAccessor)
.orElseThrow(() -> new IllegalArgumentException("Unable to find the NetworkManager field in PlayerConnection"));
}
// Retrieve the network manager

View File

@ -5,7 +5,9 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.function.Function;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
@ -15,9 +17,9 @@ import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.Origin;
@ -47,9 +49,29 @@ public final class MinecraftMethods {
// Decorated PacketSerializer to identify methods
private volatile static ConstructorAccessor decoratedDataSerializerAccessor;
private volatile static Function<ByteBuf, Object> friendlyBufBufConstructor;
private MinecraftMethods() {
// sealed
}
public static Function<ByteBuf, Object> getFriendlyBufBufConstructor() {
if (friendlyBufBufConstructor == null) {
Optional<Class<?>> registryByteBuf = MinecraftReflection.getRegistryFriendlyByteBufClass();
if (registryByteBuf.isPresent()) {
ConstructorAccessor accessor = Accessors.getConstructorAccessor(FuzzyReflection.fromClass(registryByteBuf.get()).getConstructor(FuzzyMethodContract.newBuilder()
.parameterDerivedOf(ByteBuf.class)
.parameterDerivedOf(MinecraftReflection.getRegistryAccessClass())
.build()));
friendlyBufBufConstructor = (byteBuf) -> accessor.invoke(byteBuf, MinecraftRegistryAccess.get());
} else {
ConstructorAccessor accessor = Accessors.getConstructorAccessor(MinecraftReflection.getPacketDataSerializerClass(), ByteBuf.class);
friendlyBufBufConstructor = (byteBuf) -> accessor.invoke(byteBuf);
}
}
return friendlyBufBufConstructor;
}
/**
* Retrieve the send packet method in PlayerConnection/NetServerHandler.
@ -145,6 +167,7 @@ public final class MinecraftMethods {
* Retrieve the Packet.read(PacketDataSerializer) method.
*
* @return The packet read method.
* @deprecated no longer works since 1.20.5
*/
public static MethodAccessor getPacketReadByteBufMethod() {
initializePacket();
@ -157,6 +180,7 @@ public final class MinecraftMethods {
* This only exists in version 1.7.2 and above.
*
* @return The packet write method.
* @deprecated no longer works since 1.20.5
*/
public static MethodAccessor getPacketWriteByteBufMethod() {
initializePacket();
@ -171,7 +195,7 @@ public final class MinecraftMethods {
.method(ElementMatchers.not(ElementMatchers.isDeclaredBy(Object.class)))
.intercept(MethodDelegation.to(new Object() {
@RuntimeType
public Object delegate(@SuperCall Callable<?> zuper, @Origin Method method) throws Exception {
public Object delegate(@SuperCall(nullIfImpossible = true) Callable<?> zuper, @Origin Method method) throws Exception {
if (method.getName().contains("read")) {
throw new ReadMethodException();
}
@ -195,6 +219,11 @@ public final class MinecraftMethods {
* Initialize the two read() and write() methods.
*/
private static void initializePacket() {
// write and read methods are no longer part of the packet interface since 1.20.5
if (MinecraftVersion.v1_20_5.atOrAbove()) {
throw new IllegalStateException("can't access packet read/write method after 1.20.5");
}
// Initialize the methods
if (packetReadByteBuf == null || packetWriteByteBuf == null) {
// setups a decorated PacketDataSerializer which we can use to identity read/write methods in the packet class
@ -203,7 +232,8 @@ public final class MinecraftMethods {
}
// constructs a new decorated serializer
Object decoratedSerializer = decoratedDataSerializerAccessor.invoke(Unpooled.EMPTY_BUFFER);
Object serializerBacking = decoratedDataSerializerAccessor.invoke(Unpooled.EMPTY_BUFFER);
Object decoratedSerializer = decoratedDataSerializerAccessor.invoke(serializerBacking);
// find all methods which might be the read or write methods
List<Method> candidates = FuzzyReflection

View File

@ -88,6 +88,8 @@ public final class MinecraftProtocolVersion {
map.put(new MinecraftVersion(1, 19, 4), 762);
map.put(new MinecraftVersion(1, 20, 0), 763);
map.put(new MinecraftVersion(1, 20, 1), 763);
map.put(new MinecraftVersion(1, 20, 2), 764);
return map;
}

View File

@ -30,6 +30,12 @@ import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.injector.BukkitUnwrapper;
@ -43,12 +49,9 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.wrappers.EnumWrappers;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
/**
* Methods and constants specifically used in conjuction with reflecting Minecraft object.
@ -160,7 +163,7 @@ public final class MinecraftReflection {
Matcher packageMatcher = PACKAGE_VERSION_MATCHER.matcher(CRAFTBUKKIT_PACKAGE);
if (packageMatcher.matches()) {
packageVersion = packageMatcher.group(1);
} else {
} else if (!MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) { // ignore version prefix since it's no longer needed
MinecraftVersion version = new MinecraftVersion(craftServer);
// Just assume R1 - it's probably fine (warn anyway)
@ -638,7 +641,8 @@ public final class MinecraftReflection {
}
public static boolean isBundleDelimiter(Class<?> packetClass) {
return Optionals.Equals(getBundleDelimiterClass(), packetClass);
Class<?> bundleDelimiterClass = getBundleDelimiterClass().orElse(null);
return bundleDelimiterClass != null && (packetClass.equals(bundleDelimiterClass) || bundleDelimiterClass.isAssignableFrom(packetClass));
}
public static Optional<Class<?>> getBundleDelimiterClass() {
@ -805,6 +809,10 @@ public final class MinecraftReflection {
return getNullableNMS("core.particles.ParticleType", "core.particles.SimpleParticleType", "ParticleType");
}
public static Class<?> getParticleClass() {
return getNullableNMS("core.particles.Particle");
}
/**
* Retrieve the WorldType class.
*
@ -1386,7 +1394,7 @@ public final class MinecraftReflection {
* @param aliases Potential aliases
* @return Optional that may contain the class
*/
private static Optional<Class<?>> getOptionalNMS(String className, String... aliases) {
public static Optional<Class<?>> getOptionalNMS(String className, String... aliases) {
if (minecraftPackage == null) {
minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
}
@ -1495,9 +1503,7 @@ public final class MinecraftReflection {
*/
public static Object getPacketDataSerializer(Object buffer) {
try {
// TODO: move this to MinecraftMethods, or at least, cache the constructor accessor
Class<?> packetSerializer = getPacketDataSerializerClass();
return packetSerializer.getConstructor(getByteBufClass()).newInstance(buffer);
return MinecraftMethods.getFriendlyBufBufConstructor().apply((ByteBuf) buffer);
} catch (Exception e) {
throw new RuntimeException("Cannot construct packet serializer.", e);
}
@ -1656,7 +1662,7 @@ public final class MinecraftReflection {
*/
private static void useFallbackServer() {
// Get the first constructor that matches CraftServer(MINECRAFT_OBJECT, ANY)
Constructor<?> selected = FuzzyReflection.fromClass(getCraftBukkitClass("CraftServer"))
Constructor<?> selected = FuzzyReflection.fromClass(getCraftServer())
.getConstructor(FuzzyMethodContract.newBuilder()
.parameterMatches(getMinecraftObjectMatcher(), 0)
.parameterCount(2)
@ -1712,4 +1718,36 @@ public final class MinecraftReflection {
public static Class<?> getHolderClass() {
return getMinecraftClass("core.Holder");
}
public static Class<?> getCraftServer() {
return getCraftBukkitClass("CraftServer");
}
public static Class<?> getHolderLookupProviderClass() {
return getMinecraftClass("core.HolderLookup$a" /* Spigot Mappings */, "core.HolderLookup$Provider" /* Mojang Mappings */);
}
public static Class<?> getRegistryAccessClass() {
return getMinecraftClass("core.IRegistryCustom" /* Spigot Mappings */, "core.RegistryAccess" /* Mojang Mappings */);
}
public static Class<?> getProtocolInfoClass() {
return getMinecraftClass("network.ProtocolInfo");
}
public static Class<?> getProtocolInfoUnboundClass() {
return getMinecraftClass("network.ProtocolInfo$a" /* Spigot Mappings */, "network.ProtocolInfo$Unbound" /* Mojang Mappings */);
}
public static Class<?> getPacketFlowClass() {
return getMinecraftClass("network.protocol.EnumProtocolDirection" /* Spigot Mappings */, "network.protocol.PacketFlow" /* Mojang Mappings */);
}
public static Class<?> getStreamCodecClass() {
return getMinecraftClass("network.codec.StreamCodec");
}
public static Optional<Class<?>> getRegistryFriendlyByteBufClass() {
return getOptionalNMS("network.RegistryFriendlyByteBuf");
}
}

View File

@ -0,0 +1,60 @@
package com.comphenix.protocol.utility;
import java.lang.reflect.Modifier;
import org.bukkit.Bukkit;
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;
/**
* Static getter for the registry access accessor which is need for most of the methods
* since 1.20.5 that access the registry and in form.
*/
public class MinecraftRegistryAccess {
private static MethodAccessor GET_SERVER = null;
private static MethodAccessor REGISTRY_ACCESS = null;
// lazy initialized
private static Object registryAccess = null;
static {
if (MinecraftVersion.v1_20_5.atOrAbove()) {
GET_SERVER = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(MinecraftReflection.getCraftServer(), false)
.getMethod(FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.returnDerivedOf(MinecraftReflection.getMinecraftServerClass())
.build()));
REGISTRY_ACCESS = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(MinecraftReflection.getMinecraftServerClass(), false)
.getMethod(FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.returnDerivedOf(MinecraftReflection.getRegistryAccessClass())
.build()));
}
}
/**
* Returns the composite global registry access. Equiv. of
* <pre>((CraftServer) Bukkit.getServer()).getServer().registryAccess()</pre>
*
* @return composite registy acesss
*/
public static Object get() {
if (GET_SERVER == null || REGISTRY_ACCESS == null) {
return null;
}
if (registryAccess == null) {
Object server = GET_SERVER.invoke(Bukkit.getServer());
registryAccess = REGISTRY_ACCESS.invoke(server);
}
return registryAccess;
}
}

View File

@ -36,7 +36,20 @@ import org.bukkit.Server;
* @author Kristian
*/
public final class MinecraftVersion implements Comparable<MinecraftVersion>, Serializable {
/**
* Version 1.20.5 - the cookie and transfer packet update
*/
public static final MinecraftVersion v1_20_5 = new MinecraftVersion("1.20.5");
/**
* Version 1.20.4 - the decorated pot update
*/
public static final MinecraftVersion v1_20_4 = new MinecraftVersion("1.20.4");
/**
* Version 1.20.2 - the update that added the configuration protocol phase.
*/
public static final MinecraftVersion CONFIG_PHASE_PROTOCOL_UPDATE = new MinecraftVersion("1.20.2");
/**
* Version 1.20 - the trails and tails update
*/
@ -131,7 +144,7 @@ public final class MinecraftVersion implements Comparable<MinecraftVersion>, Ser
/**
* The latest release version of minecraft.
*/
public static final MinecraftVersion LATEST = TRAILS_AND_TAILS;
public static final MinecraftVersion LATEST = v1_20_5;
// used when serializing
private static final long serialVersionUID = -8695133558996459770L;

View File

@ -1,72 +0,0 @@
package com.comphenix.protocol.utility;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import java.util.function.Consumer;
public class SchedulerUtil {
private Object foliaScheduler;
private MethodAccessor runAtFixedRate;
private MethodAccessor cancelTasks;
private MethodAccessor execute;
private static SchedulerUtil getInstance() {
return Holder.INSTANCE;
}
private static class Holder {
private static final SchedulerUtil INSTANCE = new SchedulerUtil();
}
private SchedulerUtil() {
if (Util.isUsingFolia()) {
MethodAccessor getScheduler = Accessors.getMethodAccessor(Bukkit.getServer().getClass(), "getGlobalRegionScheduler");
foliaScheduler = getScheduler.invoke(Bukkit.getServer());
runAtFixedRate = Accessors.getMethodAccessor(foliaScheduler.getClass(), "runAtFixedRate", Plugin.class,
Consumer.class, long.class, long.class);
cancelTasks = Accessors.getMethodAccessor(foliaScheduler.getClass(), "cancelTasks", Plugin.class);
execute = Accessors.getMethodAccessor(foliaScheduler.getClass(), "execute", Plugin.class, Runnable.class);
}
}
public static int scheduleSyncRepeatingTask(Plugin plugin, Runnable runnable, long delay, long period) {
return getInstance().doScheduleSyncRepeatingTask(plugin, runnable, delay, period);
}
private int doScheduleSyncRepeatingTask(Plugin plugin, Runnable runnable, long delay, long period) {
if (Util.isUsingFolia()) {
runAtFixedRate.invoke(foliaScheduler, plugin, (Consumer<Object>)(task -> runnable.run()), delay, period);
return 1;
} else {
return plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, runnable, delay, period);
}
}
private void doCancelTask(Plugin plugin, int id) {
if (Util.isUsingFolia()) {
cancelTasks.invoke(foliaScheduler, plugin);
} else {
plugin.getServer().getScheduler().cancelTask(id);
}
}
public static void cancelTask(Plugin plugin, int id) {
getInstance().doCancelTask(plugin, id);
}
private void doExecute(Plugin plugin, Runnable runnable) {
if (Util.isUsingFolia()) {
execute.invoke(foliaScheduler, plugin, runnable);
} else {
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, runnable);
}
}
public static void execute(Plugin plugin, Runnable runnable) {
getInstance().doExecute(plugin, runnable);
}
}

View File

@ -4,6 +4,7 @@ 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.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.comphenix.protocol.wrappers.nbt.NbtType;
@ -93,13 +94,24 @@ public class StreamSerializer {
*/
public void serializeCompound(DataOutputStream output, NbtCompound compound) {
if (WRITE_NBT_METHOD == null) {
WRITE_NBT_METHOD = Accessors.getMethodAccessor(FuzzyReflection
.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true)
.getMethodByParameters("writeNbtCompound", MinecraftReflection.getNBTCompoundClass()));
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getPacketDataSerializerClass(), true);
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
FuzzyMethodContract writeNbtContract = FuzzyMethodContract.newBuilder()
.returnTypeExact(MinecraftReflection.getPacketDataSerializerClass())
.parameterExactArray(MinecraftReflection.getNBTBaseClass())
.build();
WRITE_NBT_METHOD = Accessors.getMethodAccessor(fuzzy.getMethod(writeNbtContract));
} else {
WRITE_NBT_METHOD = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("writeNbtCompound", MinecraftReflection.getNBTCompoundClass()));
}
}
ByteBuf buf = NettyByteBufAdapter.packetWriter(output);
buf.writeByte(NbtType.TAG_COMPOUND.getRawID());
// 1.20.2+ will write the id automatically
if (!MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
buf.writeByte(NbtType.TAG_COMPOUND.getRawID());
}
// Get the NMS version of the compound
Object handle = compound != null ? NbtFactory.fromBase(compound).getHandle() : null;

View File

@ -23,7 +23,6 @@ public final class Util {
private static final boolean SPIGOT = classExists("org.spigotmc.SpigotConfig");
private static final boolean FOLIA = classExists("io.papermc.paper.threadedregions.RegionizedServer");
private static Class<?> cachedBundleClass;
public static boolean classExists(String className) {
try {

View File

@ -22,15 +22,8 @@ import com.comphenix.protocol.wrappers.Either.Right;
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
import java.lang.ref.WeakReference;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@ -1115,7 +1108,6 @@ public class BukkitConverters {
static MethodAccessor getSoundEffect = null;
static FieldAccessor soundKey = null;
static MethodAccessor getSoundEffectByKey = null;
static MethodAccessor getSoundEffectBySound = null;
static MethodAccessor getSoundByEffect = null;
@ -1124,16 +1116,10 @@ public class BukkitConverters {
public static EquivalentConverter<Sound> getSoundConverter() {
// Try to create sound converter for new versions greater 1.16.4
if (MinecraftVersion.NETHER_UPDATE_4.atOrAbove()) {
if (getSoundEffectByKey == null || getSoundEffectBySound == null || getSoundByEffect == null) {
if (getSoundEffectBySound == null || getSoundByEffect == null) {
Class<?> craftSound = MinecraftReflection.getCraftSoundClass();
FuzzyReflection fuzzy = FuzzyReflection.fromClass(craftSound, true);
getSoundEffectByKey = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters(
"getSoundEffect",
MinecraftReflection.getSoundEffectClass(),
String.class
));
getSoundEffectBySound = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters(
"getSoundEffect",
MinecraftReflection.getSoundEffectClass(),
@ -1164,8 +1150,9 @@ public class BukkitConverters {
try {
return (Sound) getSoundByEffect.invoke(null, generic);
} catch (IllegalStateException ex) {
if (ex.getCause() instanceof NullPointerException) {
if (ex.getCause() instanceof NullPointerException || ex.getCause() instanceof NoSuchElementException) {
// "null" sounds cause NPEs inside getSoundByEffect
// "null" sounds can also trigger a NSE in newer versions because of Optional.get() usages
return null;
}
throw ex;
@ -1361,34 +1348,62 @@ public class BukkitConverters {
@Override
public Object getGeneric(PotionEffectType specific) {
Class<?> clazz = MinecraftReflection.getMobEffectListClass();
if (getMobEffect == null) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(clazz, false);
getMobEffect = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterExactArray(int.class)
.returnTypeExact(clazz)
.requireModifier(Modifier.STATIC)
.build()));
}
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
if (getMobEffect == null) {
Class<?> potionEffectTypeClass = MinecraftReflection.getCraftBukkitClass("potion.CraftPotionEffectType");
FuzzyReflection fuzzy = FuzzyReflection.fromClass(potionEffectTypeClass, false);
getMobEffect = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterExactArray(PotionEffectType.class)
.returnTypeExact(MinecraftReflection.getMobEffectListClass())
.requireModifier(Modifier.STATIC)
.build()));
}
int id = specific.getId();
return getMobEffect.invoke(null, id);
return getMobEffect.invoke(null, specific);
} else {
if (getMobEffect == null) {
Class<?> clazz = MinecraftReflection.getMobEffectListClass();
FuzzyReflection fuzzy = FuzzyReflection.fromClass(clazz, false);
getMobEffect = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterExactArray(int.class)
.returnTypeExact(clazz)
.requireModifier(Modifier.STATIC)
.build()));
}
int id = specific.getId();
return getMobEffect.invoke(null, id);
}
}
@Override
public PotionEffectType getSpecific(Object generic) {
Class<?> clazz = MinecraftReflection.getMobEffectListClass();
if (getMobEffectId == null) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(clazz, false);
getMobEffectId = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterExactArray(clazz)
.returnTypeExact(int.class)
.requireModifier(Modifier.STATIC)
.build()));
}
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
if (getMobEffectId == null) {
Class<?> potionEffectTypeClass = MinecraftReflection.getCraftBukkitClass("potion.CraftPotionEffectType");
FuzzyReflection fuzzy = FuzzyReflection.fromClass(potionEffectTypeClass, false);
getMobEffectId = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterExactArray(MinecraftReflection.getMobEffectListClass())
.returnTypeExact(PotionEffectType.class)
.requireModifier(Modifier.STATIC)
.build()));
}
int id = (int) getMobEffectId.invoke(null, generic);
return PotionEffectType.getById(id);
return (PotionEffectType) getMobEffectId.invoke(null, generic);
} else {
if (getMobEffectId == null) {
Class<?> clazz = MinecraftReflection.getMobEffectListClass();
FuzzyReflection fuzzy = FuzzyReflection.fromClass(clazz, false);
getMobEffectId = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterExactArray(clazz)
.returnTypeExact(int.class)
.requireModifier(Modifier.STATIC)
.build()));
}
int id = (int) getMobEffectId.invoke(null, generic);
return PotionEffectType.getById(id);
}
}
});
}

View File

@ -168,6 +168,7 @@ public class Converters {
@Override
public Optional<T> getSpecific(Object generic) {
if (generic == null) return Optional.empty();
Optional<Object> optional = (Optional<Object>) generic;
return optional.map(converter::getSpecific);
}

View File

@ -0,0 +1,245 @@
package com.comphenix.protocol.wrappers;
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.ConstructorAccessor;
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.ByteBuddyGenerated;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.StreamSerializer;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Objects;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.MethodCall;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.Argument;
import net.bytebuddy.implementation.bind.annotation.FieldValue;
import net.bytebuddy.matcher.ElementMatchers;
/**
* A wrapper for the CustomPacketPayload class in 1.20.2. Due to the nature of the class, not all types are supported
* by default. Constructing a new wrapper instance will give out a handle to a completely new implemented type, that
* allows to set a key and some kind of data of any choice.
* <p>
* Note that constructing this class from a generic handle is only possible for the spigot-specific UnknownPayload type.
* All other payloads should be accessed via a structure modifier directly.
*
* @author Pasqual Koschmieder
*/
public final class CustomPacketPayloadWrapper {
private static final Class<?> MINECRAFT_KEY_CLASS;
private static final Class<?> CUSTOM_PACKET_PAYLOAD_CLASS;
private static final ConstructorAccessor PAYLOAD_WRAPPER_CONSTRUCTOR;
private static final MethodAccessor GET_ID_PAYLOAD_METHOD;
private static final MethodAccessor SERIALIZE_PAYLOAD_METHOD;
private static final EquivalentConverter<CustomPacketPayloadWrapper> CONVERTER;
static {
try {
MINECRAFT_KEY_CLASS = MinecraftReflection.getMinecraftKeyClass();
CUSTOM_PACKET_PAYLOAD_CLASS = MinecraftReflection.getMinecraftClass("network.protocol.common.custom.CustomPacketPayload");
Method getPayloadId = FuzzyReflection.fromClass(CUSTOM_PACKET_PAYLOAD_CLASS).getMethod(FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.returnTypeExact(MINECRAFT_KEY_CLASS)
.parameterCount(0)
.build());
GET_ID_PAYLOAD_METHOD = Accessors.getMethodAccessor(getPayloadId);
Method serializePayloadData = FuzzyReflection.fromClass(CUSTOM_PACKET_PAYLOAD_CLASS).getMethod(FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.returnTypeVoid()
.parameterCount(1)
.parameterDerivedOf(ByteBuf.class, 0)
.build());
SERIALIZE_PAYLOAD_METHOD = Accessors.getMethodAccessor(serializePayloadData);
Constructor<?> payloadWrapperConstructor = makePayloadWrapper();
PAYLOAD_WRAPPER_CONSTRUCTOR = Accessors.getConstructorAccessor(payloadWrapperConstructor);
CONVERTER = new EquivalentConverter<CustomPacketPayloadWrapper>() {
@Override
public Object getGeneric(CustomPacketPayloadWrapper specific) {
return specific.newHandle();
}
@Override
public CustomPacketPayloadWrapper getSpecific(Object generic) {
return fromUnknownPayload(generic);
}
@Override
public Class<CustomPacketPayloadWrapper> getSpecificType() {
return CustomPacketPayloadWrapper.class;
}
};
} catch (Exception exception) {
throw new ExceptionInInitializerError(exception);
}
}
private static Constructor<?> makePayloadWrapper() throws Exception {
return new ByteBuddy()
.subclass(Object.class)
.name("com.comphenix.protocol.wrappers.ProtocolLibCustomPacketPayload")
.implement(CUSTOM_PACKET_PAYLOAD_CLASS, ByteBuddyGenerated.class)
.defineField("payload", byte[].class, Modifier.PRIVATE | Modifier.FINAL)
.defineField("id", MinecraftReflection.getMinecraftKeyClass(), Modifier.PRIVATE | Modifier.FINAL)
.defineConstructor(Modifier.PUBLIC)
.withParameters(MinecraftReflection.getMinecraftKeyClass(), byte[].class)
.intercept(MethodCall.invoke(Object.class.getConstructor())
.andThen(FieldAccessor.ofField("id").setsArgumentAt(0))
.andThen(FieldAccessor.ofField("payload").setsArgumentAt(1)))
.method(ElementMatchers.returns(MinecraftReflection.getMinecraftKeyClass()).and(ElementMatchers.takesNoArguments()))
.intercept(FieldAccessor.ofField("id"))
.method(ElementMatchers.returns(void.class).and(ElementMatchers.takesArguments(MinecraftReflection.getPacketDataSerializerClass())))
.intercept(MethodDelegation.to(CustomPacketPayloadInterceptionHandler.class))
.make()
.load(ByteBuddyFactory.getInstance().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
.getLoaded()
.getConstructor(MinecraftReflection.getMinecraftKeyClass(), byte[].class);
}
// ====== api methods ======
/**
* The wrapped payload in the message.
*/
private final byte[] payload;
/**
* The wrapped key of the message.
*/
private final MinecraftKey id;
/**
* The generic id of the message, lazy initialized when needed.
*/
private Object genericId;
/**
* Constructs a new payload wrapper instance using the given message payload and id.
*
* @param payload the payload of the message.
* @param id the id of the message.
* @throws NullPointerException if the given payload or id is null.
*/
public CustomPacketPayloadWrapper(byte[] payload, MinecraftKey id) {
this.payload = Objects.requireNonNull(payload, "payload");
this.id = Objects.requireNonNull(id, "id");
}
/**
* Get the CustomPacketPayload class that is backing this wrapper (available since Minecraft 1.20.2).
*
* @return the CustomPacketPayload class.
*/
public static Class<?> getCustomPacketPayloadClass() {
return CUSTOM_PACKET_PAYLOAD_CLASS;
}
/**
* Get a converter to convert this wrapper to a generic handle and an UnknownPayload type to this wrapper.
*
* @return a converter for this wrapper.
*/
public static EquivalentConverter<CustomPacketPayloadWrapper> getConverter() {
return CONVERTER;
}
/**
* Constructs this wrapper from any CustomPayload type.
* <p>
* Note: the buffer of the given payload (if any) will <strong>NOT</strong> be released by this operation. Make sure
* to release the buffer manually if you discard the packet to prevent memory leaks.
*
* @param payload the instance of the custom payload to convert to this wrapper.
* @return a wrapper holding the minecraft key and payload of the given custom payload instance.
*/
public static CustomPacketPayloadWrapper fromUnknownPayload(Object payload) {
Object messageId = GET_ID_PAYLOAD_METHOD.invoke(payload);
MinecraftKey id = MinecraftKey.getConverter().getSpecific(messageId);
// we read and retain the underlying buffer in case the class uses a buffer to store the data
// this way, when passing the packet to further handling, the buffer is not released and can be re-used
StructureModifier<Object> modifier = new StructureModifier<>(payload.getClass()).withTarget(payload);
byte[] messagePayload = modifier.withType(ByteBuf.class).optionRead(0)
.map(buffer -> {
ByteBuf buf = (ByteBuf) buffer;
byte[] data = StreamSerializer.getDefault().getBytesAndRelease(buf.markReaderIndex().retain());
buf.resetReaderIndex();
return data;
})
.orElseGet(() -> {
ByteBuf buffer = Unpooled.buffer();
Object serializer = MinecraftReflection.getPacketDataSerializer(buffer);
SERIALIZE_PAYLOAD_METHOD.invoke(payload, serializer);
return StreamSerializer.getDefault().getBytesAndRelease(buffer);
});
return new CustomPacketPayloadWrapper(messagePayload, id);
}
/**
* Get the generic id of the wrapped message id.
*
* @return the generic key id.
*/
private Object getGenericId() {
if (this.genericId == null) {
this.genericId = MinecraftKey.getConverter().getGeneric(this.id);
}
return this.genericId;
}
/**
* Get the message payload of this wrapper. Changes made to the returned array will be reflected into this wrapper.
*
* @return the message payload.
*/
public byte[] getPayload() {
return this.payload;
}
/**
* Get the message id of this wrapper.
*
* @return the message id of this wrapper.
*/
public MinecraftKey getId() {
return this.id;
}
/**
* Constructs a <strong>NEW</strong> handle instance of a payload wrapper to use in a CustomPayload packet.
*
* @return a new payload wrapper instance using the provided message id and payload.
*/
public Object newHandle() {
return PAYLOAD_WRAPPER_CONSTRUCTOR.invoke(this.getGenericId(), this.payload);
}
/**
* Handles interception of the ProtocolLib specific CustomPayloadWrapper implementation. For internal use only.
*/
@SuppressWarnings("unused")
static final class CustomPacketPayloadInterceptionHandler {
public static void intercept(@FieldValue("payload") byte[] payload, @Argument(0) Object packetBuffer) {
((ByteBuf) packetBuffer).writeBytes(payload);
}
}
}

View File

@ -121,7 +121,11 @@ public abstract class EnumWrappers {
SUCCESSFULLY_LOADED,
DECLINED,
FAILED_DOWNLOAD,
ACCEPTED
ACCEPTED,
DOWNLOADED,
INVALID_URL,
FAILED_RELOAD,
DISCARDED;
}
public enum PlayerInfoAction {
@ -404,7 +408,10 @@ public abstract class EnumWrappers {
ROARING,
SNIFFING,
EMERGING,
DIGGING;
DIGGING,
SLIDING,
SHOOTING,
INHALING;
private final static EquivalentConverter<EntityPose> POSE_CONVERTER = EnumWrappers.getEntityPoseConverter();
@ -484,8 +491,6 @@ public abstract class EnumWrappers {
* Initialize the wrappers, if we haven't already.
*/
private static void initialize() {
if (INITIALIZED)
return;
@ -493,7 +498,12 @@ public abstract class EnumWrappers {
PROTOCOL_CLASS = getEnum(PacketType.Handshake.Client.SET_PROTOCOL.getPacketClass(), 0);
CLIENT_COMMAND_CLASS = getEnum(PacketType.Play.Client.CLIENT_COMMAND.getPacketClass(), 0);
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");
} else {
CHAT_VISIBILITY_CLASS = getEnum(PacketType.Play.Client.SETTINGS.getPacketClass(), 0);
}
try {
DIFFICULTY_CLASS = getEnum(PacketType.Play.Server.SERVER_DIFFICULTY.getPacketClass(), 0);
@ -501,7 +511,12 @@ public abstract class EnumWrappers {
DIFFICULTY_CLASS = getEnum(PacketType.Play.Server.LOGIN.getPacketClass(), 1);
}
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);

View File

@ -1,7 +1,7 @@
package com.comphenix.protocol.wrappers;
import com.google.gson.JsonObject;
import java.io.StringReader;
import java.util.Optional;
import org.bukkit.ChatColor;
@ -9,8 +9,12 @@ import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftRegistryAccess;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.base.Preconditions;
import com.google.gson.JsonObject;
/**
* Represents a chat component added in Minecraft 1.7.2
@ -20,6 +24,8 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
private static final Class<?> SERIALIZER = MinecraftReflection.getChatSerializerClass();
private static final Class<?> COMPONENT = MinecraftReflection.getIChatBaseComponentClass();
private static final Class<?> GSON_CLASS = MinecraftReflection.getMinecraftGsonClass();
private static final Optional<Class<?>> MUTABLE_COMPONENT_CLASS
= MinecraftReflection.getOptionalNMS("network.chat.IChatMutableComponent");
private static Object GSON = null;
private static MethodAccessor DESERIALIZE = null;
@ -30,22 +36,41 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
private static ConstructorAccessor CONSTRUCT_TEXT_COMPONENT = null;
static {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(SERIALIZER, true);
FuzzyReflection reflection = FuzzyReflection.fromClass(SERIALIZER, true);
// Retrieve the correct methods
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethodByReturnTypeAndParameters("serialize", /* a */
String.class, new Class<?>[] { COMPONENT }));
GSON = Accessors.getFieldAccessor(fuzzy.getFieldByType("gson", GSON_CLASS)).get(null);
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()) {
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(reflection.getMethod(FuzzyMethodContract.newBuilder()
.returnTypeExact(String.class)
.parameterDerivedOf(COMPONENT)
.parameterDerivedOf(MinecraftReflection.getHolderLookupProviderClass())
.build()));
} else {
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(reflection.getMethodByReturnTypeAndParameters("serialize", /* a */
String.class, new Class<?>[] { COMPONENT }));
}
GSON = Accessors.getFieldAccessor(reflection.getFieldByType("gson", GSON_CLASS)).get(null);
if (MinecraftVersion.v1_20_5.atOrAbove()) {
DESERIALIZE = Accessors.getMethodAccessor(reflection.getMethod(FuzzyMethodContract.newBuilder()
.returnDerivedOf(COMPONENT)
.parameterExactType(String.class)
.parameterDerivedOf(MinecraftReflection.getHolderLookupProviderClass())
.build()));
} else if (MinecraftVersion.v1_20_4.atOrAbove()) {
DESERIALIZE = Accessors.getMethodAccessor(reflection
.getMethodByReturnTypeAndParameters("fromJson", MUTABLE_COMPONENT_CLASS.get(), new Class[] { String.class }));
} else {
try {
DESERIALIZE = Accessors.getMethodAccessor(FuzzyReflection.fromClass(MinecraftReflection.getChatDeserializer(), true)
.getMethodByReturnTypeAndParameters("deserialize", Object.class, new Class<?>[] { GSON_CLASS, String.class, Class.class, boolean.class }));
} catch (IllegalArgumentException ex) {
// We'll handle it in the ComponentParser
DESERIALIZE = null;
}
}
// Get a component from a standard Minecraft message
CONSTRUCT_COMPONENT = Accessors.getMethodAccessor(MinecraftReflection.getCraftChatMessage(), "fromString", String.class, boolean.class);
@ -57,7 +82,23 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
}
}
private static Object serialize(Object handle) {
if (MinecraftVersion.v1_20_5.atOrAbove()) {
return SERIALIZE_COMPONENT.invoke(null, handle, MinecraftRegistryAccess.get());
}
return SERIALIZE_COMPONENT.invoke(null, handle);
}
private static Object deserialize(String json) {
if (MinecraftVersion.v1_20_5.atOrAbove()) {
return DESERIALIZE.invoke(null, json, MinecraftRegistryAccess.get());
}
if (MinecraftVersion.v1_20_4.atOrAbove()) {
return DESERIALIZE.invoke(null, json);
}
// Should be non-null on 1.9 and up
if (DESERIALIZE != null) {
return DESERIALIZE.invoke(null, GSON, json, COMPONENT, true);
@ -152,7 +193,7 @@ public class WrappedChatComponent extends AbstractWrapper implements ClonableWra
*/
public String getJson() {
if (cache == null) {
cache = (String) SERIALIZE_COMPONENT.invoke(null, handle);
cache = (String) serialize(handle);
}
return cache;
}

View File

@ -9,6 +9,7 @@ import com.comphenix.protocol.wrappers.WrappedDataWatcher.Serializer;
/**
* Represents a DataValue in 1.19.3+.
* Use {@link WrappedWatchableObject} before 1.19.3.
*/
public class WrappedDataValue extends AbstractWrapper {
@ -29,10 +30,28 @@ public class WrappedDataValue extends AbstractWrapper {
this.modifier = new StructureModifier<>(this.handleType).withTarget(handle);
}
/**
* Creates a new WrappedDataValue from a NMS value.
* ProtocolLib wrappers are not supported as arguments.
* If implicit unwrapping of wrappers is required, use {@link WrappedDataValue#fromWrappedValue(int, Serializer, Object)}.
* @param index the index of the metadata value
* @param serializer the serializer corresponding for serializing. Can be null.
* @param value The raw value for the DataValue. Can be null.
*/
public WrappedDataValue(int index, Serializer serializer, Object value) {
this(newHandle(index, serializer, value));
}
/**
* Creates a new WrappedDataValue from a possibly wrapped value and implicitly unwrap value if possible.
* @param index the index of the metadata value
* @param serializer the serializer corresponding for serializing. Can be null.
* @param value The value for the DataValue. Can be null.
*/
public static WrappedDataValue fromWrappedValue(int index, Serializer serializer, Object value) {
return new WrappedDataValue(index, serializer, value == null ? null : WrappedWatchableObject.getUnwrapped(value));
}
private static Object newHandle(int index, Serializer serializer, Object value) {
if (constructor == null) {
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE.getConstructors()[0]);
@ -41,14 +60,26 @@ public class WrappedDataValue extends AbstractWrapper {
return constructor.invoke(index, serializer.getHandle(), value);
}
/**
* Returns the entity-type specific index of this DataValue
* @return index of the DataValue
*/
public int getIndex() {
return this.modifier.<Integer>withType(int.class).read(0);
}
/**
* Sets the entity-type specific index of this DataValue
* @param index New index of the DataValue
*/
public void setIndex(int index) {
this.modifier.withType(int.class).write(0, index);
}
/**
* Returns the current serializer for this DataValue.
* @return serializer
*/
public Serializer getSerializer() {
Object serializer = this.modifier.readSafely(1);
if (serializer != null) {
@ -63,22 +94,42 @@ public class WrappedDataValue extends AbstractWrapper {
}
}
/**
* Changes the serializer for this DataValue
* @param serializer serializer
*/
public void setSerializer(Serializer serializer) {
this.modifier.writeSafely(1, serializer == null ? null : serializer.getHandle());
}
/**
* Returns the current value associated and implicitly wraps it to corresponding ProtocolLib wrappers if possible.
* @return Current value
*/
public Object getValue() {
return WrappedWatchableObject.getWrapped(getRawValue());
}
/**
* Sets the current value associated and implicitly unwraps it to NMS types if a ProtocolLib wrapper is provided.
* @param value New value for this DataValue
*/
public void setValue(Object value) {
setRawValue(WrappedWatchableObject.getUnwrapped(value));
}
/**
* Returns the current, raw value.
* @return Raw value (not wrapped)
*/
public Object getRawValue() {
return this.modifier.readSafely(2);
}
/**
* Updates the raw value for this DataValue. No unwrapping will be applied.
* @param value NMS value
*/
public void setRawValue(Object value) {
this.modifier.writeSafely(2, value);
}

View File

@ -9,7 +9,9 @@ import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.instances.MinecraftGenerator;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.collection.ConvertedMultimap;
import com.google.common.base.Objects;
import com.google.common.collect.Multimap;
@ -112,15 +114,7 @@ public class WrappedGameProfile extends AbstractWrapper {
*/
@Deprecated
public WrappedGameProfile(String id, String name) {
super(GAME_PROFILE);
if (CREATE_STRING_STRING != null) {
setHandle(CREATE_STRING_STRING.invoke(id, name));
} else if (CREATE_UUID_STRING != null) {
setHandle(CREATE_UUID_STRING.invoke(parseUUID(id), name));
} else {
throw new IllegalArgumentException("Unsupported GameProfile constructor.");
}
this(parseUUID(id), name);
}
/**
@ -137,7 +131,17 @@ public class WrappedGameProfile extends AbstractWrapper {
if (CREATE_STRING_STRING != null) {
setHandle(CREATE_STRING_STRING.invoke(uuid != null ? uuid.toString() : null, name));
} else if (CREATE_UUID_STRING != null) {
setHandle(CREATE_UUID_STRING.invoke(uuid, name));
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
// 1.20.2+ requires all fields to have a value: null uuid -> UUID(0,0), null name -> empty name
// it's not allowed to pass null for both, so we need to pre-check that
if (uuid == null && (name == null || name.isEmpty())) {
throw new IllegalArgumentException("Name and ID cannot both be blank");
}
setHandle(CREATE_UUID_STRING.invoke(uuid == null ? MinecraftGenerator.SYS_UUID : uuid, name == null ? "" : name));
} else {
setHandle(CREATE_UUID_STRING.invoke(uuid, name));
}
} else {
throw new IllegalArgumentException("Unsupported GameProfile constructor.");
}
@ -197,6 +201,10 @@ public class WrappedGameProfile extends AbstractWrapper {
uuid = parseUUID(getId());
} else if (GET_ID != null) {
uuid = (UUID) GET_ID.invoke(handle);
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove() && MinecraftGenerator.SYS_UUID.equals(uuid)) {
// see CraftPlayerProfile
uuid = null;
}
} else {
throw new IllegalStateException("Unsupported getId() method");
}
@ -224,7 +232,7 @@ public class WrappedGameProfile extends AbstractWrapper {
if (GET_UUID_STRING != null) {
return (String) GET_UUID_STRING.get(handle);
} else if (GET_ID != null) {
UUID uuid = (UUID) GET_ID.invoke(handle);
UUID uuid = getUUID();
return uuid != null ? uuid.toString() : null;
} else {
throw new IllegalStateException("Unsupported getId() method");
@ -238,7 +246,12 @@ public class WrappedGameProfile extends AbstractWrapper {
*/
public String getName() {
if (GET_NAME != null) {
return (String) GET_NAME.invoke(handle);
String name = (String) GET_NAME.invoke(handle);
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove() && name != null && name.isEmpty()) {
// see CraftPlayerProfile
name = null;
}
return name;
} else {
throw new IllegalStateException("Unsupported getName() method");
}

View File

@ -1,23 +1,24 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import org.jetbrains.annotations.Nullable;
import com.comphenix.protocol.injector.StructureCache;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.utility.ZeroBuffer;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.google.common.collect.Lists;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
/**
* Wrapper classes for ClientboundLevelChunkWithLightPacket
@ -45,8 +46,11 @@ public final class WrappedLevelChunkData {
static {
FuzzyReflection reflection = FuzzyReflection.fromClass(HANDLE_TYPE, true);
LEVEL_CHUNK_PACKET_DATA_CONSTRUCTOR = Accessors.getConstructorAccessor(HANDLE_TYPE,
MinecraftReflection.getPacketDataSerializerClass(), int.class, int.class);
LEVEL_CHUNK_PACKET_DATA_CONSTRUCTOR = Accessors.getConstructorAccessor(reflection.getConstructor(FuzzyMethodContract.newBuilder()
.parameterDerivedOf(MinecraftReflection.getPacketDataSerializerClass())
.parameterExactType(int.class)
.parameterExactType(int.class)
.build()));
BLOCK_ENTITIES_DATA_ACCESSOR = Accessors.getFieldAccessor(reflection.getField(FuzzyFieldContract.newBuilder()
.typeExact(List.class)
.build()));
@ -160,8 +164,11 @@ public final class WrappedLevelChunkData {
static {
FuzzyReflection reflection = FuzzyReflection.fromClass(HANDLE_TYPE, true);
LIGHT_UPDATE_PACKET_DATA_CONSTRUCTOR = Accessors.getConstructorAccessor(HANDLE_TYPE,
MinecraftReflection.getPacketDataSerializerClass(), int.class, int.class);
LIGHT_UPDATE_PACKET_DATA_CONSTRUCTOR = Accessors.getConstructorAccessor(reflection.getConstructor(FuzzyMethodContract.newBuilder()
.parameterDerivedOf(MinecraftReflection.getPacketDataSerializerClass())
.parameterExactType(int.class)
.parameterExactType(int.class)
.build()));
BIT_SET_ACCESSORS = Accessors.getFieldAccessorArray(HANDLE_TYPE, BitSet.class, true);
BYTE_ARRAY_LIST_ACCESSORS = Accessors.getFieldAccessorArray(HANDLE_TYPE, List.class, true);
@ -333,7 +340,7 @@ public final class WrappedLevelChunkData {
*/
public static LightData fromValues(BitSet skyYMask, BitSet blockYMask, BitSet emptySkyYMask, BitSet emptyBlockYMask,
List<byte[]> skyUpdates, List<byte[]> blockUpdates) {
LightData data = new LightData(LIGHT_UPDATE_PACKET_DATA_CONSTRUCTOR.invoke(MinecraftReflection.getPacketDataSerializer(new ZeroBuffer()), 0, 0));
LightData data = new LightData(LIGHT_UPDATE_PACKET_DATA_CONSTRUCTOR.invoke(StructureCache.newNullDataSerializer(), 0, 0));
data.setSkyYMask(skyYMask);
data.setBlockYMask(blockYMask);

View File

@ -11,6 +11,7 @@ import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import org.bukkit.Color;
import org.bukkit.Particle;
import org.bukkit.block.data.BlockData;
import org.bukkit.inventory.ItemStack;
/**
@ -20,6 +21,7 @@ public class WrappedParticle<T> {
private static Class<?> VECTOR_3FA;
private static MethodAccessor toBukkit;
private static MethodAccessor getType;
private static MethodAccessor toNMS;
private static MethodAccessor toCraftData;
@ -29,15 +31,33 @@ public class WrappedParticle<T> {
}
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getCraftBukkitClass("CraftParticle"));
FuzzyMethodContract contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(Particle.class)
.parameterExactType(MinecraftReflection.getParticleParam())
.build();
toBukkit = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
FuzzyMethodContract contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(Particle.class)
.parameterExactArray(MinecraftReflection.getParticleClass())
.build();
toBukkit = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
contract = FuzzyMethodContract
FuzzyReflection particleParam = FuzzyReflection.fromClass(MinecraftReflection.getParticleParam(), false);
contract = FuzzyMethodContract
.newBuilder()
.returnTypeExact(MinecraftReflection.getParticleClass())
.parameterCount(0)
.build();
getType = Accessors.getMethodAccessor(particleParam.getMethod(contract));
} else {
FuzzyMethodContract contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(Particle.class)
.parameterExactType(MinecraftReflection.getParticleParam())
.build();
toBukkit = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
}
FuzzyMethodContract contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(MinecraftReflection.getParticleParam())
@ -117,28 +137,26 @@ public class WrappedParticle<T> {
public static WrappedParticle fromHandle(Object handle) {
ensureMethods();
Particle bukkit = (Particle) toBukkit.invoke(null, handle);
Object data = null;
switch (bukkit) {
case BLOCK_CRACK:
case BLOCK_DUST:
case FALLING_DUST:
data = getBlockData(handle);
break;
case ITEM_CRACK:
data = getItem(handle);
break;
case REDSTONE:
data = getRedstone(handle);
break;
case DUST_COLOR_TRANSITION:
data = getDustTransition(handle);
break;
default:
break;
Particle bukkit;
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
Object particle = getType.invoke(handle);
bukkit = (Particle) toBukkit.invoke(null, particle);
} else {
bukkit = (Particle) toBukkit.invoke(null, handle);
}
Object data = null;
Class<?> dataType = bukkit.getDataType();
if (dataType == BlockData.class) {
data = getBlockData(handle);
} else if (dataType == Particle.DustTransition.class) {
data = getDustTransition(handle);
} else if (dataType == ItemStack.class) {
data = getItem(handle);
} else if (dataType == Particle.DustOptions.class) {
data = getRedstone(handle);
}
return new WrappedParticle<>(handle, bukkit, data);
}
@ -199,8 +217,8 @@ public class WrappedParticle<T> {
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(0);
org.joml.Vector3f fromRGB = (org.joml.Vector3f) modifier.withType(org.joml.Vector3f.class).read(1);
org.joml.Vector3f toRGB = (org.joml.Vector3f) modifier.withType(org.joml.Vector3f.class).read(1);
org.joml.Vector3f fromRGB = (org.joml.Vector3f) modifier.withType(org.joml.Vector3f.class).read(0);
size = (float) modifier.withType(float.class).read(0);
fromR = (int) (fromRGB.x() * 255);

View File

@ -134,8 +134,7 @@ public class WrappedServerPing implements ClonableWrapper {
* @return The favicon, or NULL if no favicon will be displayed.
*/
public CompressedImage getFavicon() {
String favicon = impl.getFavicon();
return (favicon != null) ? CompressedImage.fromEncodedText(favicon) : null;
return impl.getFavicon();
}
/**
@ -143,7 +142,7 @@ public class WrappedServerPing implements ClonableWrapper {
* @param image - the new compressed image or NULL if no favicon should be displayed.
*/
public void setFavicon(CompressedImage image) {
impl.setFavicon((image != null) ? image.toEncodedText() : null);
impl.setFavicon(image);
}
/**

View File

@ -5,6 +5,7 @@ import java.security.PublicKey;
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.MinecraftVersion;
import com.google.common.base.Objects;
/**
@ -36,16 +37,23 @@ public class WrappedSignedProperty extends AbstractWrapper {
try {
CONSTRUCTOR = Accessors.getConstructorAccessor(PROPERTY, String.class, String.class, String.class);
GET_NAME = Accessors.getMethodAccessor(PROPERTY, "getName");
GET_SIGNATURE = Accessors.getMethodAccessor(PROPERTY, "getSignature");
GET_VALUE = Accessors.getMethodAccessor(PROPERTY, "getValue");
HAS_SIGNATURE = Accessors.getMethodAccessor(PROPERTY, "hasSignature");
IS_SIGNATURE_VALID = Accessors.getMethodAccessor(PROPERTY, "isSignatureValid", PublicKey.class);
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
GET_NAME = Accessors.getMethodAccessorOrNull(PROPERTY, "name");
GET_SIGNATURE = Accessors.getMethodAccessor(PROPERTY, "signature");
GET_VALUE = Accessors.getMethodAccessor(PROPERTY, "value");
} else {
GET_NAME = Accessors.getMethodAccessorOrNull(PROPERTY, "getName");
GET_SIGNATURE = Accessors.getMethodAccessor(PROPERTY, "getSignature");
GET_VALUE = Accessors.getMethodAccessor(PROPERTY, "getValue");
}
} catch (Throwable ex) {
throw new RuntimeException("Failed to obtain methods for Property.", ex);
}
}
/**
* Construct a new wrapped signed property from the given values.
* @param name - the name of the property.

View File

@ -0,0 +1,41 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Wrapper for StreamCodec class which is primarily used to de-/serialize
* packets since 1.20.5
*/
public class WrappedStreamCodec extends AbstractWrapper {
// use the de-/encoder interfaces to get the right method to avoid future errors
private static final Class<?> DECODER_TYPE = MinecraftReflection.getMinecraftClass("network.codec.StreamDecoder");
private static final Class<?> ENCODER_TYPE = MinecraftReflection.getMinecraftClass("network.codec.StreamEncoder");
private static final MethodAccessor DECODE_ACCESSOR;
private static final MethodAccessor ENCODE_ACCESSOR;
static {
DECODE_ACCESSOR = Accessors.getMethodAccessor(FuzzyReflection.fromClass(DECODER_TYPE)
.getMethodByReturnTypeAndParameters("decode", Object.class, new Class[] { Object.class }));
ENCODE_ACCESSOR = Accessors.getMethodAccessor(FuzzyReflection.fromClass(ENCODER_TYPE)
.getMethodByReturnTypeAndParameters("encode", Void.TYPE, new Class[] { Object.class, Object.class }));
}
public WrappedStreamCodec(Object handle) {
super(MinecraftReflection.getStreamCodecClass());
setHandle(handle);
}
public Object decode(Object buffer) {
return DECODE_ACCESSOR.invoke(handle, buffer);
}
public void encode(Object buffer, Object value) {
ENCODE_ACCESSOR.invoke(handle, buffer, value);
}
}

View File

@ -14,8 +14,6 @@
*/
package com.comphenix.protocol.wrappers;
import static com.comphenix.protocol.utility.MinecraftReflection.is;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
@ -25,12 +23,15 @@ import com.comphenix.protocol.wrappers.WrappedDataWatcher.Serializer;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import java.util.Optional;
import org.bukkit.inventory.ItemStack;
import java.util.Optional;
import static com.comphenix.protocol.utility.MinecraftReflection.is;
/**
* Represents a DataWatcher Item in 1.8 to 1.10.
*
* Represents a DataWatcher Item in 1.8 to 1.19.2.
* Use {@link WrappedDataValue} for 1.19.3 or later.
* @author dmulloy2
*/
public class WrappedWatchableObject extends AbstractWrapper {
@ -97,6 +98,9 @@ public class WrappedWatchableObject extends AbstractWrapper {
* @return The wrapped object.
*/
static Object getWrapped(Object value) {
if(value == null) {
return null;
}
// Handle watcher items first
if (is(MinecraftReflection.getDataWatcherItemClass(), value)) {
return getWrapped(new WrappedWatchableObject(value).getRawValue());
@ -135,6 +139,9 @@ public class WrappedWatchableObject extends AbstractWrapper {
*/
// Must be kept in sync with getWrapped!
static Object getUnwrapped(Object wrapped) {
if(wrapped == null) {
return null;
}
if (wrapped instanceof Optional<?>) {
return ((Optional<?>) wrapped).map(WrappedWatchableObject::getUnwrapped);
}

View File

@ -4,7 +4,9 @@ import com.comphenix.protocol.reflect.FieldAccessException;
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.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.nbt.NbtBase;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
@ -22,13 +24,10 @@ public class NbtBinarySerializer {
public static final NbtBinarySerializer DEFAULT = new NbtBinarySerializer();
private static final Class<?> NBT_BASE_CLASS = MinecraftReflection.getNBTBaseClass();
// Used to read and write NBT
private static MethodAccessor methodWrite;
/**
* Method selected for loading NBT compounds.
* Method selected for loading/writing NBT compounds.
*/
private static LoadMethod loadMethod;
private static CodecMethod codecMethod;
private static MethodAccessor getNbtLoadMethod(Class<?>... parameters) {
Method method = getUtilityClass().getMethodByReturnTypeAndParameters("load", NBT_BASE_CLASS, parameters);
@ -39,6 +38,18 @@ public class NbtBinarySerializer {
return FuzzyReflection.fromClass(MinecraftReflection.getNbtCompressedStreamToolsClass(), true);
}
private static CodecMethod getCodecMethod() {
if (codecMethod == null) {
// Save the selected method
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
codecMethod = new LoadMethodConfigPhaseUpdate();
} else {
codecMethod = new LoadMethodSkinUpdate();
}
}
return codecMethod;
}
/**
* Write the content of a wrapped NBT tag to a stream.
*
@ -47,14 +58,7 @@ public class NbtBinarySerializer {
* @param destination - the destination stream.
*/
public <T> void serialize(NbtBase<T> value, DataOutput destination) {
if (methodWrite == null) {
Class<?> base = MinecraftReflection.getNBTBaseClass();
Method writeNBT = getUtilityClass().getMethodByParameters("writeNBT", base, DataOutput.class);
methodWrite = Accessors.getMethodAccessor(writeNBT);
}
methodWrite.invoke(null, NbtFactory.fromBase(value).getHandle(), destination);
getCodecMethod().writeNbt(NbtFactory.fromBase(value).getHandle(), destination);
}
/**
@ -65,13 +69,8 @@ public class NbtBinarySerializer {
* @return An NBT tag.
*/
public <TType> NbtWrapper<TType> deserialize(DataInput source) {
if (loadMethod == null) {
// Save the selected method
loadMethod = new LoadMethodSkinUpdate();
}
try {
return NbtFactory.fromNMS(loadMethod.loadNbt(source), null);
return NbtFactory.fromNMS(getCodecMethod().loadNbt(source), null);
} catch (Exception e) {
throw new FieldAccessException("Unable to read NBT from " + source, e);
}
@ -100,7 +99,7 @@ public class NbtBinarySerializer {
return (NbtList<T>) (NbtBase<?>) this.deserialize(source);
}
private interface LoadMethod {
private interface CodecMethod {
/**
* Load an NBT compound from a given stream.
@ -109,20 +108,73 @@ public class NbtBinarySerializer {
* @return The loaded NBT compound.
*/
Object loadNbt(DataInput input);
/**
* Write an NBT compound to the given stream.
*
* @param nbt the nbt to write.
* @param target the target to write the compound to.
*/
void writeNbt(Object nbt, DataOutput target);
}
/**
* Load an NBT compound from the NBTCompressedStreamTools static method since 1.7.
*/
private static class LoadMethodSkinUpdate implements LoadMethod {
private static class LoadMethodSkinUpdate implements CodecMethod {
private final Class<?> readLimitClass = MinecraftReflection.getNBTReadLimiterClass();
private final Object readLimiter = FuzzyReflection.fromClass(this.readLimitClass).getSingleton();
private final MethodAccessor accessor = getNbtLoadMethod(DataInput.class, int.class, this.readLimitClass);
private final MethodAccessor readNbt = getNbtLoadMethod(DataInput.class, int.class, this.readLimitClass);
private final MethodAccessor writeNBT = Accessors.getMethodAccessor(getUtilityClass().getMethodByParameters("writeNBT", MinecraftReflection.getNBTBaseClass(), DataOutput.class));
@Override
public Object loadNbt(DataInput input) {
return this.accessor.invoke(null, input, 0, this.readLimiter);
return this.readNbt.invoke(null, input, 0, this.readLimiter);
}
@Override
public void writeNbt(Object nbt, DataOutput target) {
this.writeNBT.invoke(null, nbt, target);
}
}
/**
* Load an NBT compound from the NBTCompressedStreamTools static method since 1.20.2.
*/
private static class LoadMethodConfigPhaseUpdate implements CodecMethod {
private final Class<?> readLimitClass = MinecraftReflection.getNBTReadLimiterClass();
private final Object readLimiter = FuzzyReflection.fromClass(this.readLimitClass).getSingleton();
private final MethodAccessor readNbt;
private final MethodAccessor writeNbt;
public LoadMethodConfigPhaseUpdate() {
// there are now two methods with the same signature: readAnyTag/readUnnamedTag & writeAnyTag/writeUnnamedTag
// we can only find the correct method here by using the method name... thanks Mojang
Method readNbtMethod = getUtilityClass().getMethod(FuzzyMethodContract.newBuilder()
.nameExact("b")
.returnTypeExact(MinecraftReflection.getNBTBaseClass())
.parameterExactArray(DataInput.class, this.readLimitClass)
.build());
this.readNbt = Accessors.getMethodAccessor(readNbtMethod);
Method writeNbtMethod = getUtilityClass().getMethod(FuzzyMethodContract.newBuilder()
.nameExact("a")
.parameterExactArray(MinecraftReflection.getNBTBaseClass(), DataOutput.class)
.build());
this.writeNbt = Accessors.getMethodAccessor(writeNbtMethod);
}
@Override
public Object loadNbt(DataInput input) {
return this.readNbt.invoke(null, input, this.readLimiter);
}
@Override
public void writeNbt(Object nbt, DataOutput target) {
this.writeNbt.invoke(null, nbt, target);
}
}
}

View File

@ -155,8 +155,10 @@ public final class LegacyServerPing extends AbstractWrapper implements ServerPin
* @return The favicon, or NULL if no favicon will be displayed.
*/
@Override
public String getFavicon() {
return (String) FAVICON.get(handle);
public WrappedServerPing.CompressedImage getFavicon() {
String favicon = (String) FAVICON.get(handle);
return (favicon != null) ? WrappedServerPing.CompressedImage.fromEncodedText(favicon) : null;
}
/**
@ -164,8 +166,8 @@ public final class LegacyServerPing extends AbstractWrapper implements ServerPin
* @param image - the new compressed image or NULL if no favicon should be displayed.
*/
@Override
public void setFavicon(String image) {
FAVICON.set(handle, image);
public void setFavicon(WrappedServerPing.CompressedImage image) {
FAVICON.set(handle, (image != null) ? image.toEncodedText() : null);
}
/**

View File

@ -2,6 +2,7 @@ package com.comphenix.protocol.wrappers.ping;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedServerPing;
import com.google.common.collect.ImmutableList;
public interface ServerPingImpl extends Cloneable {
@ -17,8 +18,8 @@ public interface ServerPingImpl extends Cloneable {
void setVersionName(String versionName);
int getVersionProtocol();
void setVersionProtocol(int protocolVersion);
String getFavicon();
void setFavicon(String favicon);
WrappedServerPing.CompressedImage getFavicon();
void setFavicon(WrappedServerPing.CompressedImage favicon);
boolean isEnforceSecureChat();
void setEnforceSecureChat(boolean safeChat);

View File

@ -273,13 +273,13 @@ public final class ServerPingRecord implements ServerPingImpl {
}
@Override
public String getFavicon() {
return new String(favicon.iconBytes, StandardCharsets.UTF_8);
public WrappedServerPing.CompressedImage getFavicon() {
return new WrappedServerPing.CompressedImage("data:image/png;base64", favicon.iconBytes);
}
@Override
public void setFavicon(String favicon) {
this.favicon.iconBytes = favicon.getBytes(StandardCharsets.UTF_8);
public void setFavicon(WrappedServerPing.CompressedImage favicon) {
this.favicon.iconBytes = favicon.getDataCopy();
}
@Override

View File

@ -2,24 +2,53 @@ package com.comphenix.protocol;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.utility.MinecraftReflectionTestUtil;
import com.google.common.util.concurrent.MoreExecutors;
import net.minecraft.SharedConstants;
import net.minecraft.commands.CommandDispatcher;
import net.minecraft.core.IRegistry;
import net.minecraft.core.IRegistryCustom;
import net.minecraft.core.LayeredRegistryAccess;
import net.minecraft.resources.RegistryDataLoader;
import net.minecraft.server.DataPackResources;
import net.minecraft.server.DispenserRegistry;
import net.minecraft.server.Main;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.RegistryLayer;
import net.minecraft.server.WorldLoader;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.dedicated.DedicatedServerProperties;
import net.minecraft.server.level.WorldServer;
import net.minecraft.server.packs.EnumResourcePackType;
import net.minecraft.server.packs.repository.ResourcePackLoader;
import net.minecraft.server.packs.repository.ResourcePackRepository;
import net.minecraft.server.packs.repository.ResourcePackSourceVanilla;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.world.flag.FeatureFlagSet;
import net.minecraft.world.flag.FeatureFlags;
import net.minecraft.world.item.enchantment.Enchantments;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_20_R1.util.Versioning;
import org.bukkit.craftbukkit.v1_20_R4.CraftLootTable;
import org.bukkit.craftbukkit.v1_20_R4.CraftRegistry;
import org.bukkit.craftbukkit.v1_20_R4.CraftServer;
import org.bukkit.craftbukkit.v1_20_R4.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_20_R4.util.CraftMagicNumbers;
import org.bukkit.craftbukkit.v1_20_R4.util.CraftNamespacedKey;
import org.bukkit.craftbukkit.v1_20_R4.util.Versioning;
import org.spigotmc.SpigotWorldConfig;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -69,8 +98,31 @@ public class BukkitInitialization {
instance.setPackage();
SharedConstants.a();
DispenserRegistry.a();
// Minecraft Data Init
SharedConstants.a(); // .tryDetectVersion()
DispenserRegistry.a(); // .bootStrap()
ResourcePackRepository resourcePackRepository = ResourcePackSourceVanilla.c(); // .createVanillaTrustedRepository()
resourcePackRepository.a(); // .reload()
ResourceManager resourceManager = new ResourceManager(
EnumResourcePackType.b /* SERVER_DATA */,
resourcePackRepository.c() /* getAvailablePacks() */ .stream().map(ResourcePackLoader::f /* openFull() */).collect(Collectors.toList()));
LayeredRegistryAccess<RegistryLayer> layeredRegistryAccess = RegistryLayer.a(); // .createRegistryAccess()
layeredRegistryAccess = WorldLoader.b(resourceManager, layeredRegistryAccess, RegistryLayer.b /* WORLDGEN */, RegistryDataLoader.a /* WORLDGEN_REGISTRIES */); // .loadAndReplaceLayer()
IRegistryCustom.Dimension registryCustom = layeredRegistryAccess.a().d(); // .compositeAccess().freeze()
// IRegistryCustom.Dimension registryCustom = layeredRegistryAccess.a().c(); // .compositeAccess().freeze()
DataPackResources dataPackResources = DataPackResources.a(
resourceManager,
layeredRegistryAccess,
FeatureFlagSet.a() /* REGISTRY.allFlags() */,
CommandDispatcher.ServerType.b /* DEDICATED */,
0,
MoreExecutors.directExecutor(),
MoreExecutors.directExecutor()
).join();
// dataPackResources.a(registryCustom); // .updateRegistryTags()
try {
IRegistry.class.getName();
@ -82,18 +134,30 @@ public class BukkitInitialization {
String serverVersion = CraftServer.class.getPackage().getImplementationVersion();
// Mock the server object
Server mockedServer = mock(Server.class);
CraftServer mockedServer = mock(CraftServer.class);
DedicatedServer mockedGameServer = mock(DedicatedServer.class);
when(mockedGameServer.bc()/*registryAccess*/).thenReturn(registryCustom);
when(mockedServer.getLogger()).thenReturn(java.util.logging.Logger.getLogger("Minecraft"));
when(mockedServer.getName()).thenReturn("Mock Server");
when(mockedServer.getVersion()).thenReturn(serverVersion + " (MC: " + releaseTarget + ")");
when(mockedServer.getBukkitVersion()).thenReturn(Versioning.getBukkitVersion());
when(mockedServer.getServer()).thenReturn(mockedGameServer);
when(mockedServer.getItemFactory()).thenReturn(CraftItemFactory.instance());
when(mockedServer.isPrimaryThread()).thenReturn(true);
when(mockedServer.getItemFactory()).thenReturn(CraftItemFactory.instance());
when(mockedServer.getUnsafe()).thenReturn(CraftMagicNumbers.INSTANCE);
/*when(mockedServer.getLootTable(any())).thenAnswer(invocation -> {
NamespacedKey key = invocation.getArgument(0);
return new CraftLootTable(key, dataPackResources.b().b(CraftNamespacedKey.toMinecraft(key)));
});*/
when(mockedServer.getRegistry(any())).thenAnswer(invocation -> {
Class<Keyed> registryType = invocation.getArgument(0);
return CraftRegistry.createRegistry(registryType, registryCustom);
});
WorldServer nmsWorld = mock(WorldServer.class);
SpigotWorldConfig mockWorldConfig = mock(SpigotWorldConfig.class);
try {
@ -109,8 +173,13 @@ public class BukkitInitialization {
List<World> worlds = Collections.singletonList(world);
when(mockedServer.getWorlds()).thenReturn(worlds);
// Inject this fake server
// Inject this fake server & our registry (must happen after server set)
Bukkit.setServer(mockedServer);
CraftRegistry.setMinecraftRegistry(registryCustom);
// Init Enchantments
Enchantments.A.getClass();
// Enchantment.stopAcceptingRegistrations();
initialized = true;
}

View File

@ -14,98 +14,82 @@
*/
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.assertTrue;
import java.util.ArrayList;
import java.util.List;
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.Test;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftReflectionTestUtil;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import net.minecraft.network.EnumProtocol;
import net.minecraft.network.protocol.EnumProtocolDirection;
import net.minecraft.network.protocol.login.PacketLoginInStart;
import org.apache.commons.lang.WordUtils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Field;
import java.util.*;
import java.util.Map.Entry;
import static org.junit.jupiter.api.Assertions.*;
/**
* @author dmulloy2
*/
public class PacketTypeTest {
private static final Pattern PACKET_PATTERN = Pattern.compile("(?<sender>Serverbound|Clientbound)(?<name>\\w+)Packet");
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
// I'm well aware this is jank, but it does in fact work correctly and give the desired result
/*PacketType.onDynamicCreate = className -> {
/* PacketType.onDynamicCreate = className -> {
throw new RuntimeException("Dynamically generated packet " + className);
};*/
}; */
}
@AfterAll
public static void afterClass() {
PacketType.onDynamicCreate = __ -> {
PacketType.onDynamicCreate = (x, y) -> {
};
}
@SuppressWarnings("unchecked")
// @Test
// public static void main(String[] args) throws Exception {
public void generateNewPackets() throws Exception {
MinecraftReflectionTestUtil.init();
public static void main(String[] args) throws Exception {
// public void generateNewPackets() throws Exception {
BukkitInitialization.initializeAll();
Set<Class<?>> allTypes = new HashSet<>();
List<Class<?>> newTypes = new ArrayList<>();
PacketType.onDynamicCreate = (type, className) -> {
String packetTypeClassName = className;
EnumProtocol[] protocols = EnumProtocol.values();
for (EnumProtocol protocol : protocols) {
System.out.println(WordUtils.capitalize(protocol.name().toLowerCase()));
Matcher matcher = PACKET_PATTERN.matcher(className);
if (matcher.find()) {
if (!matcher.group("sender").equals(type.getSender().getMojangName())) {
throw new RuntimeException(String.format("wrong packet flow, exepected: %s, got: %s", type.getSender().getMojangName(), matcher.group("sender")));
}
packetTypeClassName = matcher.group("name");
}
Field field = EnumProtocol.class.getDeclaredField("k");
field.setAccessible(true);
System.out.printf("%s, %s = new PacketType(PROTOCOL, SENDER, %s, \"%s\") %s\n", type.getProtocol(), type.getSender(), formatHex(type.getCurrentId()), packetTypeClassName, className);
};
Map<EnumProtocolDirection, Object> map = (Map<EnumProtocolDirection, Object>) field.get(protocol);
for (Entry<EnumProtocolDirection, Object> entry : map.entrySet()) {
Field mapField = entry.getValue().getClass().getDeclaredField("b");
mapField.setAccessible(true);
Map<Class<?>, Integer> reverseMap = (Map<Class<?>, Integer>) mapField.get(entry.getValue());
Map<Integer, Class<?>> treeMap = new TreeMap<>();
for (Entry<Class<?>, Integer> entry1 : reverseMap.entrySet()) {
treeMap.put(entry1.getValue(), entry1.getKey());
}
System.out.println(" " + entry.getKey());
for (Entry<Integer, Class<?>> entry1 : treeMap.entrySet()) {
System.out.println(generateNewType(entry1.getKey(), entry1.getValue()));
allTypes.add(entry1.getValue());
try {
PacketType.fromClass(entry1.getValue());
} catch (Exception ex) {
newTypes.add(entry1.getValue());
}
}
}
}
System.out.println("New types: " + newTypes);
PacketType.onIdMismatch = (type, newId) -> {
System.out.printf("%s, %s, %s %s MISMTACH %s\n", type.getProtocol(), type.getSender(), type.name(), formatHex(type.getCurrentId()), formatHex(newId));
};
// initialize packet registry
PacketRegistry.getClientPacketTypes();
for (PacketType type : PacketType.values()) {
if (type.isDeprecated()) {
continue;
}
if (!allTypes.contains(type.getPacketClass())) {
if (type.getPacketClass() == null) {
System.out.println(type + " was removed");
}
}
@ -286,73 +270,35 @@ public class PacketTypeTest {
}
@Test
@SuppressWarnings("unchecked")
public void ensureTypesAreCorrect() throws Exception {
PacketType.onDynamicCreate = className -> {
throw new RuntimeException("Dynamically generated packet " + className);
};
public void ensureRegistryInitializes() throws Exception {
try {
boolean fail = false;
PacketType.onDynamicCreate = (type, className) -> {
throw new RuntimeException("Dynamically generated packet " + className);
};
EnumProtocol[] protocols = EnumProtocol.values();
for (EnumProtocol protocol : protocols) {
Field field = EnumProtocol.class.getDeclaredField("k");
field.setAccessible(true);
Map<EnumProtocolDirection, Object> map = (Map<EnumProtocolDirection, Object>) field.get(protocol);
for (Entry<EnumProtocolDirection, Object> entry : map.entrySet()) {
Field mapField = entry.getValue().getClass().getDeclaredField("b");
mapField.setAccessible(true);
Map<Class<?>, Integer> reverseMap = (Map<Class<?>, Integer>) mapField.get(entry.getValue());
Map<Integer, Class<?>> treeMap = new TreeMap<>();
for (Entry<Class<?>, Integer> entry1 : reverseMap.entrySet()) {
treeMap.put(entry1.getValue(), entry1.getKey());
}
for (Entry<Integer, Class<?>> entry1 : treeMap.entrySet()) {
try {
PacketType type = PacketType.fromClass(entry1.getValue());
if (type.getCurrentId() != entry1.getKey()) {
throw new IllegalStateException(
"Packet ID for " + type + " is incorrect. Expected " + entry1.getKey() + ", but got "
+ type.getCurrentId());
}
} catch (Throwable ex) {
if (ex.getMessage().contains("BundleDelimiterPacket")) {
continue;
}
ex.printStackTrace();
fail = true;
}
}
}
}
assertFalse(fail, "Packet type(s) were incorrect!");
} finally {
PacketType.onDynamicCreate = __ -> { };
}
// try to initialize packet registry
PacketRegistry.getClientPacketTypes();
} finally {
PacketType.onDynamicCreate = (x, y) -> { };
}
}
@Test
public void testPacketCreation() {
boolean fail = false;
for (PacketType type : PacketType.values()) {
if (type.isSupported()) {
try {
new PacketContainer(type);
} catch (Exception ex) {
ex.printStackTrace();
fail = true;
}
}
}
assertFalse(fail, "Packet type(s) failed to instantiate");
}
@Test
public void testPacketCreation() {
List<PacketType> failed = new ArrayList<>();
for (PacketType type : PacketType.values()) {
if (!type.isSupported()) {
continue;
}
try {
new PacketContainer(type);
} catch (Exception ex) {
failed.add(type);
}
}
assertTrue(failed.isEmpty(), "Failed to create: " + failed);
}
@Test
public void testPacketBundleWriting() {
@ -361,7 +307,7 @@ public class PacketTypeTest {
List<PacketContainer> bundle = new ArrayList<>();
PacketContainer chatMessage = new PacketContainer(PacketType.Play.Server.SYSTEM_CHAT);
chatMessage.getStrings().write(0, WrappedChatComponent.fromText("Test").getJson());
chatMessage.getChatComponents().write(0, WrappedChatComponent.fromText("Test"));
chatMessage.getBooleans().write(0, false);
bundle.add(chatMessage);
bundlePacket.getPacketBundles().write(0, bundle);

View File

@ -15,9 +15,12 @@
*/
package com.comphenix.protocol.events;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -50,19 +53,25 @@ import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.google.common.collect.Lists;
import io.netty.buffer.ByteBuf;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.hover.content.Text;
import net.minecraft.core.Holder;
import net.minecraft.core.IRegistry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.PacketDataSerializer;
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket;
import net.minecraft.network.protocol.common.custom.BrandPayload;
import net.minecraft.network.protocol.game.PacketPlayOutGameStateChange;
import net.minecraft.network.protocol.game.PacketPlayOutUpdateAttributes;
import net.minecraft.network.protocol.game.PacketPlayOutUpdateAttributes.AttributeSnapshot;
import net.minecraft.resources.MinecraftKey;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectList;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.ai.attributes.AttributeBase;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.animal.CatVariant;
@ -79,7 +88,6 @@ import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static com.comphenix.protocol.utility.TestUtils.assertItemCollectionsEqual;
@ -153,12 +161,6 @@ public class PacketContainerTest {
assertArrayEquals(testArray, bytes.read(0));
}
@Test
public void testGetBytes() {
PacketContainer spawnMob = new PacketContainer(PacketType.Play.Server.NAMED_ENTITY_SPAWN);
this.testPrimitive(spawnMob.getBytes(), 0, (byte) 0, (byte) 1);
}
@Test
public void testGetShorts() {
PacketContainer itemData = new PacketContainer(PacketType.Play.Server.REL_ENTITY_MOVE);
@ -362,7 +364,7 @@ public class PacketContainerTest {
chatPacket.getChatComponents().write(0,
WrappedChatComponent.fromChatMessage("You shall not " + ChatColor.ITALIC + "pass!")[0]);
assertEquals("{\"extra\":[{\"text\":\"You shall not \"},{\"italic\":true,\"text\":\"pass!\"}],\"text\":\"\"}",
assertEquals("{\"text\":\"\",\"extra\":[\"You shall not \",{\"text\":\"pass!\",\"italic\":true}]}",
chatPacket.getChatComponents().read(0).getJson());
}
@ -393,29 +395,94 @@ public class PacketContainerTest {
@Test
public void testBigPacketSerialization() {
PacketContainer payload = new PacketContainer(PacketType.Play.Server.CUSTOM_PAYLOAD);
payload.getMinecraftKeys().write(0, new com.comphenix.protocol.wrappers.MinecraftKey("test"));
byte[] randomData = new byte[8192];
ThreadLocalRandom.current().nextBytes(randomData);
ByteBuf serializer = (ByteBuf) MinecraftReflection.createPacketDataSerializer(randomData.length);
serializer.writeBytes(randomData);
payload.getModifier().withType(MinecraftReflection.getPacketDataSerializerClass()).write(0, serializer);
CustomPacketPayloadWrapper payloadWrapper = new CustomPacketPayloadWrapper(randomData, new com.comphenix.protocol.wrappers.MinecraftKey("test"));
payload.getCustomPacketPayloads().write(0, payloadWrapper);
PacketContainer cloned = SerializableCloner.clone(payload);
com.comphenix.protocol.wrappers.MinecraftKey clonedKey = cloned.getMinecraftKeys().read(0);
byte[] clonedData = new byte[randomData.length];
ByteBuf clonedBuffer = (ByteBuf) cloned.getModifier()
.withType(MinecraftReflection.getPacketDataSerializerClass())
.read(0);
clonedBuffer.readBytes(clonedData);
assertEquals("minecraft:test", clonedKey.getFullKey());
assertArrayEquals(randomData, clonedData);
Assertions.assertNotSame(payload, cloned);
}
/*
@Test
public void testUnknownPayloadDeserialize() {
MinecraftKey id = new MinecraftKey("test");
byte[] payloadData = new byte[]{0x00, 0x01, 0x05, 0x07};
ByteBuf buffer = Unpooled.wrappedBuffer(payloadData);
ServerboundCustomPayloadPacket.UnknownPayload payload = new ServerboundCustomPayloadPacket.UnknownPayload(id, buffer);
ServerboundCustomPayloadPacket packet = new ServerboundCustomPayloadPacket(payload);
PacketContainer packetContainer = new PacketContainer(PacketType.Play.Client.CUSTOM_PAYLOAD, packet);
CustomPacketPayloadWrapper payloadWrapper = packetContainer.getCustomPacketPayloads().read(0);
com.comphenix.protocol.wrappers.MinecraftKey key = payloadWrapper.getId();
Assertions.assertEquals("minecraft", key.getPrefix());
Assertions.assertEquals("test", key.getKey());
Assertions.assertArrayEquals(payloadData, payloadWrapper.getPayload());
}
*/
/*
@Test
public void testCustomPayloadPacket() {
byte[] customPayload = "Hello World, This is A Super-Cool-Test!!!!!".getBytes(StandardCharsets.UTF_8);
com.comphenix.protocol.wrappers.MinecraftKey key = new com.comphenix.protocol.wrappers.MinecraftKey("protocollib", "test");
CustomPacketPayloadWrapper payloadWrapper = new CustomPacketPayloadWrapper(customPayload, key);
PacketContainer container = new PacketContainer(PacketType.Play.Server.CUSTOM_PAYLOAD);
container.getCustomPacketPayloads().write(0, payloadWrapper);
PacketDataSerializer serializer = new PacketDataSerializer(Unpooled.buffer());
ClientboundCustomPayloadPacket constructedHandle = (ClientboundCustomPayloadPacket) container.getHandle();
constructedHandle.a(serializer);
ServerboundCustomPayloadPacket deserializedHandle = new ServerboundCustomPayloadPacket(serializer);
PacketContainer serverContainer = new PacketContainer(PacketType.Play.Client.CUSTOM_PAYLOAD, deserializedHandle);
CustomPacketPayloadWrapper deserializedPayloadWrapper = serverContainer.getCustomPacketPayloads().read(0);
Assertions.assertEquals(key, deserializedPayloadWrapper.getId());
Assertions.assertArrayEquals(customPayload, deserializedPayloadWrapper.getPayload());
}
*/
/*
@Test
public void testSomeCustomPayloadRead() {
BrandPayload payload = new BrandPayload("Hello World!");
ClientboundCustomPayloadPacket handle = new ClientboundCustomPayloadPacket(payload);
PacketContainer container = new PacketContainer(PacketType.Play.Server.CUSTOM_PAYLOAD, handle);
CustomPacketPayloadWrapper payloadWrapper = container.getCustomPacketPayloads().read(0);
com.comphenix.protocol.wrappers.MinecraftKey payloadId = payloadWrapper.getId();
Assertions.assertEquals(BrandPayload.a.toString(), payloadId.getFullKey());
PacketDataSerializer serializer = new PacketDataSerializer(Unpooled.wrappedBuffer(payloadWrapper.getPayload()));
BrandPayload deserializedPayload = new BrandPayload(serializer);
Assertions.assertEquals(payload.b(), deserializedPayload.b());
}
*/
/*
@Test
public void testUnknownPayloadNotReleasedOnRead() {
MinecraftKey id = new MinecraftKey("plib", "main");
ByteBuf data = Unpooled.wrappedBuffer("This is a Test!!".getBytes(StandardCharsets.UTF_8));
ServerboundCustomPayloadPacket.UnknownPayload payload = new ServerboundCustomPayloadPacket.UnknownPayload(id, data);
ServerboundCustomPayloadPacket handle = new ServerboundCustomPayloadPacket(payload);
PacketContainer container = new PacketContainer(PacketType.Play.Client.CUSTOM_PAYLOAD, handle);
CustomPacketPayloadWrapper payloadWrapper = container.getCustomPacketPayloads().read(0);
Assertions.assertEquals(id.toString(), payloadWrapper.getId().getFullKey());
Assertions.assertEquals("This is a Test!!", new String(payloadWrapper.getPayload()));
Assertions.assertEquals(1, payload.data().refCnt());
Assertions.assertEquals(0, payload.data().readerIndex());
}
*/
@Test
public void testIntList() {
PacketContainer destroy = new PacketContainer(PacketType.Play.Server.ENTITY_DESTROY);
@ -443,8 +510,9 @@ public class PacketContainerTest {
// are inner classes (which is ultimately pointless because AttributeSnapshots don't access any
// members of the packet itself)
PacketPlayOutUpdateAttributes packet = (PacketPlayOutUpdateAttributes) attribute.getHandle();
AttributeBase base = BuiltInRegistries.v.a(MinecraftKey.a("generic.max_health"));
AttributeSnapshot snapshot = new AttributeSnapshot(base, 20.0D, modifiers);
IRegistry<AttributeBase> registry = BuiltInRegistries.u;
AttributeBase base = registry.a(MinecraftKey.a("generic.max_health"));
AttributeSnapshot snapshot = new AttributeSnapshot(Holder.a(base), 20.0D, modifiers);
attribute.getSpecificModifier(List.class).write(0, Lists.newArrayList(snapshot));
PacketContainer cloned = attribute.deepClone();
@ -493,7 +561,7 @@ public class PacketContainerTest {
@SuppressWarnings("deprecation")
public void testPotionEffect() {
PotionEffect effect = new PotionEffect(PotionEffectType.FIRE_RESISTANCE, 20 * 60, 1);
MobEffect mobEffect = new MobEffect(MobEffectList.a(effect.getType().getId()), effect.getDuration(),
MobEffect mobEffect = new MobEffect(MobEffects.l, effect.getDuration(),
effect.getAmplifier(), effect.isAmbient(),
effect.hasParticles());
int entityId = 42;
@ -509,7 +577,7 @@ public class PacketContainerTest {
WrappedRegistry registry = WrappedRegistry.getRegistry(MinecraftReflection.getMobEffectListClass());
Object effectList = assertInstanceOf(MobEffectList.class, packet.getStructures().read(0).getHandle());
assertEquals(effect.getType().getId(), registry.getId(effectList));
assertEquals(effect.getType().getId(), registry.getId(effectList) + 1); // +1 is correct, see CraftPotionEffectType
int e = 0;
if (effect.isAmbient()) {
@ -825,6 +893,7 @@ public class PacketContainerTest {
// Make sure watchable collections can be cloned
if (type == PacketType.Play.Server.ENTITY_METADATA) {
IRegistry<CatVariant> catVariantRegistry = BuiltInRegistries.ak;
constructed.getDataValueCollectionModifier().write(0, Lists.newArrayList(
new WrappedDataValue(0, Registry.get(Byte.class), (byte) 1),
new WrappedDataValue(0, Registry.get(Float.class), 5F),
@ -838,10 +907,10 @@ public class PacketContainerTest {
0,
Registry.getItemStackSerializer(false),
BukkitConverters.getItemStackConverter().getGeneric(new ItemStack(Material.WOODEN_AXE))),
new WrappedDataValue(0, Registry.get(CatVariant.class), BuiltInRegistries.aj.e(CatVariant.e)),
new WrappedDataValue(0, Registry.get(CatVariant.class), catVariantRegistry.e(CatVariant.e)),
new WrappedDataValue(0, Registry.get(FrogVariant.class), FrogVariant.a)
));
} else if (type == PacketType.Play.Server.CHAT) {
} else if (type == PacketType.Play.Server.CHAT || type == PacketType.Login.Server.DISCONNECT) {
constructed.getChatComponents().write(0, ComponentConverter.fromBaseComponent(TEST_COMPONENT));
} else if (type == PacketType.Play.Server.REMOVE_ENTITY_EFFECT || type == PacketType.Play.Server.ENTITY_EFFECT) {
constructed.getEffectTypes().write(0, PotionEffectType.GLOWING);

View File

@ -12,8 +12,8 @@ import net.minecraft.server.level.PlayerChunkMap;
import net.minecraft.server.level.PlayerChunkMap.EntityTracker;
import net.minecraft.server.level.WorldServer;
import net.minecraft.world.entity.Entity;
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_20_R4.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R4.entity.CraftEntity;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@ -35,7 +35,7 @@ public class EntityUtilitiesTest {
when(bukkit.getHandle()).thenReturn(world);
ChunkProviderServer provider = mock(ChunkProviderServer.class);
when(world.k()).thenReturn(provider);
when(world.l()).thenReturn(provider);
PlayerChunkMap chunkMap = mock(PlayerChunkMap.class);
Field chunkMapField = FuzzyReflection.fromClass(ChunkProviderServer.class, true)

View File

@ -0,0 +1,26 @@
package com.comphenix.protocol.injector;
import com.comphenix.protocol.BukkitInitialization;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class StructureCacheTests {
@BeforeAll
public static void beforeAll() {
BukkitInitialization.initializeAll();
}
@Test
public void testInitTrickSerializer() {
try {
StructureCache.initTrickDataSerializer();
} catch (IllegalStateException ex) {
// no exception or an already injected exception means it succeeded
assertTrue(ex.getMessage().contains("Cannot inject already loaded type"));
}
}
}

View File

@ -0,0 +1,35 @@
package com.comphenix.protocol.injector.netty.channel;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import io.netty.channel.Channel;
import io.netty.channel.local.LocalServerChannel;
import net.minecraft.network.EnumProtocol;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.protocol.EnumProtocolDirection;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class ChannelProtocolUtilTest {
@BeforeAll
public static void beforeClass() {
BukkitInitialization.initializeAll();
}
/*
@Test
public void testProtocolResolving() {
Channel channel = new LocalServerChannel();
channel.attr(NetworkManager.e).set(EnumProtocol.e.b(EnumProtocolDirection.a)); // ATTRIBUTE_SERVERBOUND_PROTOCOL -> Protocol.CONFIG.codec(SERVERBOUND)
channel.attr(NetworkManager.f).set(EnumProtocol.b.b(EnumProtocolDirection.b)); // ATTRIBUTE_CLIENTBOUND_PROTOCOL -> Protocol.PLAY.codec(CLIENTBOUND)
PacketType.Protocol serverBoundProtocol = ChannelProtocolUtil.PROTOCOL_RESOLVER.apply(channel, PacketType.Sender.CLIENT);
Assertions.assertEquals(PacketType.Protocol.CONFIGURATION, serverBoundProtocol);
PacketType.Protocol clientBoundProtocol = ChannelProtocolUtil.PROTOCOL_RESOLVER.apply(channel, PacketType.Sender.SERVER);
Assertions.assertEquals(PacketType.Protocol.PLAY, clientBoundProtocol);
}
*/
}

View File

@ -0,0 +1,41 @@
package com.comphenix.protocol.injector.packet;
import java.util.ArrayList;
import java.util.List;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class PacketRegistryTests {
@BeforeAll
public static void beforeAll() {
BukkitInitialization.initializeAll();
}
@Test
public void testRegistryInit() {
PacketRegistry.reset();
// completing without exception
PacketRegistry.initialize();
}
@Test
public void testAllPacketsRegistered() {
List<PacketType> missing = new ArrayList<>();
for (PacketType type : PacketType.values()) {
if (type.isDeprecated()) {
continue;
}
if (!PacketRegistry.tryGetPacketClass(type).isPresent()) {
missing.add(type);
}
}
assertTrue(missing.isEmpty(), "Missing packets: " + missing);
}
}

View File

@ -28,7 +28,7 @@ public class AggregateClonerTest {
// @Test
// Usages of NonNullList were removed in 1.17.1
public void testNonNullList() {
/* public void testNonNullList() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.WINDOW_ITEMS);
NonNullList<ItemStack> list = NonNullList.a(16, ItemStack.b);
@ -41,5 +41,5 @@ public class AggregateClonerTest {
assertEquals(list.size(), list1.size());
Assertions.assertArrayEquals(list.toArray(), list1.toArray());
}
} */
}

View File

@ -31,8 +31,12 @@ public class MinecraftMethodsTest {
this.setNull("packetReadByteBuf");
this.setNull("packetWriteByteBuf");
assertNotNull(MinecraftMethods.getPacketWriteByteBufMethod());
// TODO it's now a constructor
// assertNotNull(MinecraftMethods.getPacketReadByteBufMethod());
// the write and read method got replaced by the StreamCodec class and each
// packet now has it's own unique codec instance
if (!MinecraftVersion.v1_20_5.atOrAbove()) {
assertNotNull(MinecraftMethods.getPacketWriteByteBufMethod());
// TODO it's now a constructor
// assertNotNull(MinecraftMethods.getPacketReadByteBufMethod());
}
}
}

View File

@ -13,7 +13,7 @@ import net.minecraft.world.level.ChunkCoordIntPair;
import net.minecraft.world.level.block.state.IBlockData;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftItemStack;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.junit.jupiter.api.AfterAll;

View File

@ -2,8 +2,8 @@ package com.comphenix.protocol.utility;
public class MinecraftReflectionTestUtil {
public static final String RELEASE_TARGET = "1.20";
public static final String PACKAGE_VERSION = "v1_20_R1";
public static final String RELEASE_TARGET = "1.20.4";
public static final String PACKAGE_VERSION = "v1_20_R4";
public static final String NMS = "net.minecraft";
public static final String OBC = "org.bukkit.craftbukkit." + PACKAGE_VERSION;

View File

@ -46,10 +46,9 @@ class MinecraftVersionTest {
assertTrue(atLeast.isAtLeast(MinecraftVersion.BOUNTIFUL_UPDATE));
}
@Test
void testCurrent() {
assertEquals(MinecraftVersion.TRAILS_AND_TAILS, MinecraftVersion.getCurrentVersion());
assertEquals(MinecraftVersion.v1_20_5, MinecraftVersion.getCurrentVersion());
}
@Test

View File

@ -1,5 +1,7 @@
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.assertInstanceOf;
@ -33,7 +35,7 @@ public class AutoWrapperTest {
display.title = WrappedChatComponent.fromText("Test123");
display.description = WrappedChatComponent.fromText("Test567");
display.item = new ItemStack(Material.GOLD_INGOT);
display.background = new MinecraftKey("test");
display.background = Optional.of(new MinecraftKey("test"));
display.frameType = WrappedFrameType.CHALLENGE;
display.announceChat = false;
display.showToast = true;
@ -46,7 +48,8 @@ public class AutoWrapperTest {
assertTrue(nms.h());
assertTrue(nms.j());
assertFalse(nms.i());
assertEquals("test", nms.d().a());
assertTrue(nms.d().isPresent());
assertEquals("test", nms.d().get().a());
validateRawText(nms.a(), "Test123");
validateRawText(nms.b(), "Test567");
assertSame(AdvancementFrameType.b, nms.e());
@ -61,7 +64,7 @@ public class AutoWrapperTest {
(net.minecraft.world.item.ItemStack)MinecraftReflection.getMinecraftItemStack(new ItemStack(Material.ENDER_EYE)),
IChatBaseComponent.b("Test123"),
IChatBaseComponent.b("Test567"),
new net.minecraft.resources.MinecraftKey("minecraft", "test"),
Optional.of(new net.minecraft.resources.MinecraftKey("minecraft", "test")),
AdvancementFrameType.b,
true,
false,
@ -74,9 +77,10 @@ public class AutoWrapperTest {
assertTrue(wrapped.showToast);
assertTrue(wrapped.hidden);
assertFalse(wrapped.announceChat);
assertEquals("test", wrapped.background.getKey());
assertEquals("{\"text\":\"Test123\"}", wrapped.title.getJson());
assertEquals("{\"text\":\"Test567\"}", wrapped.description.getJson());
assertTrue(wrapped.background.isPresent());
assertEquals("test", wrapped.background.get().getKey());
assertEquals("\"Test123\"", wrapped.title.getJson());
assertEquals("\"Test567\"", wrapped.description.getJson());
assertSame(WrappedFrameType.CHALLENGE, wrapped.frameType);
assertSame(Material.ENDER_EYE, wrapped.item.getType());
assertEquals(5f, wrapped.x, 0f);
@ -89,14 +93,14 @@ public class AutoWrapperTest {
.field(0, BukkitConverters.getWrappedChatComponentConverter())
.field(1, BukkitConverters.getWrappedChatComponentConverter())
.field(2, BukkitConverters.getItemStackConverter())
.field(3, MinecraftKey.getConverter())
.field(3, Converters.optional(MinecraftKey.getConverter()))
.field(4, EnumWrappers.getGenericConverter(getMinecraftClass("advancements.AdvancementFrameType", "advancements.FrameType"),
WrappedFrameType.class));
}
private void validateRawText(IChatBaseComponent component, String expected) {
LiteralContents content = assertInstanceOf(LiteralContents.class, component.b());
assertEquals(expected, content.a());
assertEquals(expected, content.b());
}
public enum WrappedFrameType {
@ -110,7 +114,7 @@ public class AutoWrapperTest {
public WrappedChatComponent title;
public WrappedChatComponent description;
public ItemStack item;
public MinecraftKey background;
public Optional<MinecraftKey> background;
public WrappedFrameType frameType;
public boolean showToast;
public boolean announceChat;

View File

@ -14,6 +14,7 @@ import org.bukkit.enchantments.Enchantment;
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 java.util.Random;
@ -30,7 +31,7 @@ public class BukkitConvertersTest {
@Test
public void testItemStacks() {
ItemStack item = new ItemStack(Material.DIAMOND_SWORD, 16);
item.addEnchantment(Enchantment.DAMAGE_ALL, 4);
item.addEnchantment(Enchantment.POWER, 4);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.GREEN + "Diamond Sword");
item.setItemMeta(meta);
@ -61,6 +62,7 @@ public class BukkitConvertersTest {
}
@Test
@Disabled("Fails due to shared packet classes between protocol states")
public void testPacketContainerConverter() {
for (PacketType type : PacketType.values()) {
if(!type.isSupported()) {

View File

@ -17,7 +17,7 @@ import static org.junit.jupiter.api.Assertions.fail;
public class EnumWrappersTest {
private static final Set<String> KNOWN_INVALID = Sets.newHashSet(
"Particle", "WorldBorderAction", "CombatEventType", "TitleAction", "ChatType", "TitleAction"
"Particle", "WorldBorderAction", "CombatEventType", "TitleAction", "ChatType", "TitleAction", "ScoreboardAction"
);
@BeforeAll

View File

@ -8,6 +8,8 @@ import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.wrappers.WrappedAttributeModifier.Operation;
import com.google.common.collect.Lists;
import net.minecraft.core.Holder;
import net.minecraft.core.IRegistry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.protocol.game.PacketPlayOutUpdateAttributes.AttributeSnapshot;
import net.minecraft.resources.MinecraftKey;
@ -93,8 +95,9 @@ public class WrappedAttributeTest {
modifiers.add((AttributeModifier) wrapper.getHandle());
}
AttributeBase base = BuiltInRegistries.v.a(MinecraftKey.a(attribute.getAttributeKey()));
return new AttributeSnapshot(base, attribute.getBaseValue(), modifiers);
IRegistry<AttributeBase> registry = BuiltInRegistries.u;
AttributeBase base = registry.a(MinecraftKey.a(attribute.getAttributeKey()));
return new AttributeSnapshot(Holder.a(base), attribute.getBaseValue(), modifiers);
}
private AttributeModifier getModifierCopy(WrappedAttributeModifier modifier) {

View File

@ -19,9 +19,9 @@ import net.minecraft.world.level.block.state.IBlockData;
import org.bukkit.Material;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.type.GlassPane;
import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_20_R1.block.impl.CraftStainedGlassPane;
import org.bukkit.craftbukkit.v1_20_R1.util.CraftMagicNumbers;
import org.bukkit.craftbukkit.v1_20_R4.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_20_R4.block.impl.CraftStainedGlassPane;
import org.bukkit.craftbukkit.v1_20_R4.util.CraftMagicNumbers;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@ -56,7 +56,7 @@ public class WrappedBlockDataTest {
@Test
public void testDataCreation() {
IBlockData nmsData = CraftMagicNumbers.getBlock(Material.CYAN_STAINED_GLASS_PANE).n();
IBlockData nmsData = CraftMagicNumbers.getBlock(Material.CYAN_STAINED_GLASS_PANE).o();
GlassPane data = (GlassPane) CraftBlockData.fromData(nmsData);
data.setFace(BlockFace.EAST, true);

View File

@ -19,8 +19,8 @@ import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Serializer;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
import net.minecraft.world.entity.projectile.EntityEgg;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftEgg;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_20_R4.entity.CraftEgg;
import org.bukkit.craftbukkit.v1_20_R4.entity.CraftEntity;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

View File

@ -81,8 +81,8 @@ public class WrappedGameProfileTest {
PropertyMap properties = profile.getProperties();
Property property = properties.get(name).iterator().next();
assertEquals(property.getName(), name);
assertEquals(property.getValue(), value);
assertEquals(property.getSignature(), signature);
assertEquals(property.name(), name);
assertEquals(property.value(), value);
assertEquals(property.signature(), signature);
}
}

View File

@ -26,7 +26,7 @@ public class WrappedParticleTest {
public void testBlockData() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.WORLD_PARTICLES);
WrappedParticle before = WrappedParticle.create(Particle.BLOCK_CRACK,
WrappedParticle before = WrappedParticle.create(Particle.BLOCK,
WrappedBlockData.createData(Material.LAPIS_BLOCK));
packet.getNewParticles().write(0, before);
@ -38,7 +38,7 @@ public class WrappedParticleTest {
@Test
public void testItemStacks() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.WORLD_PARTICLES);
WrappedParticle before = WrappedParticle.create(Particle.ITEM_CRACK, new ItemStack(Material.FLINT_AND_STEEL));
WrappedParticle before = WrappedParticle.create(Particle.ITEM, new ItemStack(Material.FLINT_AND_STEEL));
packet.getNewParticles().write(0, before);
WrappedParticle after = packet.getNewParticles().read(0);
@ -49,7 +49,7 @@ public class WrappedParticleTest {
@Test
public void testRedstone() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.WORLD_PARTICLES);
WrappedParticle before = WrappedParticle.create(Particle.REDSTONE, new DustOptions(Color.BLUE, 1));
WrappedParticle before = WrappedParticle.create(Particle.DUST, new DustOptions(Color.BLUE, 1));
packet.getNewParticles().write(0, before);
WrappedParticle after = packet.getNewParticles().read(0);

View File

@ -1 +0,0 @@
mock-maker-inline