Compare commits

..
Author SHA1 Message Date
Dan Mulloy 39808bc64f wip: dedicated paper plugin 2025-08-21 21:34:27 -05:00
586 changed files with 1534 additions and 36399 deletions

No files matched your search

-1
View File
@@ -1,2 +1 @@
* text=auto eol=lf
.github/workflows/*.lock.yml linguist-generated=true merge=ours
+36 -70
View File
@@ -1,72 +1,38 @@
# Contributing to ProtocolLib
Contributing to ProtocolLib
===========================
This page will detail specific things that must be done if you intend to contribute to this project.
ProtocolLib supports a wide range of Minecraft versions through reflection and compatibility wrappers. Changes to packet
registration, reflection, or public wrappers can affect plugins on versions other than the one currently used for
development, so keep changes focused and preserve existing behavior unless a break is unavoidable.
## Updating ProtocolLib to a New Protocol Version
#### Before we get started:
1. **When do we need to update the protocol version?**
_A Minecraft protocol version is a number for each specification of the Minecraft protocol.
This means it is incremented when there is a change to the [Protocol Specification](http://wiki.vg/Protocol),
but not necessarily for each Minecraft version update. If the latest Minecraft version contains a protocol change,
that means ProtocolLib needs to be updated._
2. **When do we need to update other parts of ProtocolLib?**
_When the package version guard changes. The package version guard is the version string inserted
into the package identifier for CraftBukkit and net.minecraft.server (e.g. v1_9_R2). It is incremented
by Spigot if there is a major breaking change in the internal server code. This is to encourage plugins
that interact directly with CraftBukkit/nms code to update more specifically. However, since most of
ProtocolLib uses reflection, and the structure of packet classes is generally static, one will have
to go through the code and change imports from nms/CraftBukkit._
## Building ProtocolLib
Use the Gradle wrapper from the repository root. The build uses the Java 25 toolchain and resolves the current Spigot
server dependency from the CodeMC repository.
```shell
./gradlew test
./gradlew build shadowJar
```
`shadowJar` produces `build/libs/ProtocolLib.jar` with the required Byte Buddy classes included.
ProtocolLib uses the Java 25 toolchain to compile against the current server, but production classes target Java 17 for
compatibility with older supported servers.
## Updating ProtocolLib for a Minecraft Release
Start by reviewing the [Java Edition protocol](https://minecraft.wiki/w/Java_Edition_protocol) and the corresponding
server classes. The server packet classes and codecs are authoritative when documentation and implementation differ.
### 1. Update version metadata
- Update `mcVersion` in `build.gradle.kts`.
- Add the release to `MinecraftVersion` and update `MinecraftVersion.LATEST`.
- Update `ProtocolLibrary.MAXIMUM_MINECRAFT_VERSION` and `ProtocolLibrary.MINECRAFT_LAST_RELEASE_DATE`.
- Update `MinecraftProtocolVersion` if the release changed the protocol number.
### 2. Reconcile packet types
`PacketType` must contain a matching definition for every packet in the current server protocol registries. Compare the
serverbound and clientbound packet classes and codecs against every protocol phase, including configuration.
- Add new, renamed, or split packets and update their current IDs and aliases.
- Do not remove an existing public `PacketType` constant. Move removed types to the deprecated section and document their
replacement when one exists.
- Represent split packets as separate packet types instead of redirecting incompatible packet shapes.
- Run the packet registry tests after changing packet definitions. A missing definition can prevent later packets in the
same protocol registry from being registered.
### 3. Update reflection and wrappers
- Inspect changed fields, constructors, records, nested classes, and stream codecs instead of relying only on class names.
- Prefer structural or type-based reflection where names are unstable.
- Put version checks at the narrowest compatibility boundary and leave the older path unchanged.
- If an NMS concept was removed, report it as unsupported or absent rather than treating a different class as equivalent.
- Preserve packet fields that are unrelated to the value being changed.
- Keep converters symmetric and preserve wrapper cloning and equality semantics.
- A custom `StructureModifier` must maintain the complete modifier contract, including safe reads and writes, field count,
metadata, introspection, defaults, and `withTarget()`.
### 4. Add regression coverage
- Initialize the test environment with `BukkitInitialization` when tests access CraftBukkit or NMS classes.
- Exercise the actual packet structure supplied by the current Spigot dependency.
- Cover both sides of a version guard when feasible.
- Re-enable previously disabled tests when the underlying incompatibility is fixed.
- Run targeted tests while developing, followed by `./gradlew test`.
- Before declaring a Minecraft release supported, build the shaded plugin and test it on the corresponding server.
## Pull Request Expectations
- Keep the change limited to the reported problem or release update.
- Include regression tests for packet, reflection, converter, or wrapper behavior changes.
- Preserve public API signatures and binary compatibility where possible.
- Clearly document any unavoidable compatibility change.
- Do not hide reflection failures with broad exception handling or silent fallbacks.
#### Ready? Let's get started!
1. Read the [Protocol Changes](http://wiki.vg/Protocol_History). Always make sure the list is both
complete and correct. If you're unsure, don't hesitate to ask in #mcdevs (the people who maintain wiki.vg) on [libera.chat](http://libera.chat)
or #spigot on [irc.spi.gt](http://irc.spi.gt) ([webchat](https://irc.spi.gt/iris/?channels=spigot)).
2. Search for usages of the now-defunct NMS package guard and change them.
3. The class `com.comphenix.protocol.PacketType` contains a list of all the packets. If any packets were added or removed
(or had their ID changed), make sure to update this list. If a packet was removed
in favor of usage of another packet, instead of removing it, move it to the bottom of the list
in its section, add a deprecation warning to it, and redirect it to the packet that replaced it.
4. `mvn` in the root directory to build the project.
5. If tests fail in the maven build, go through the tests to make sure you removed references to any removed packets
and changed the NMS version guards.
6. Increment the package version in `com.comphenix.protocol.utility.Constants`.
7. `com.comphenix.protocol.ProtocolLibrary` contains several constants that must be updated, including the Minecraft version
and the release date.
8. `com.comphenix.protocol.utility.MinecraftProtocolVersion` contains a map of all the protocol version integers.
If the protocol version has been incremented, add a new line to the map.
9. `mvn` in root directory again. If it builds successfully, test on the appropriate version of a Spigot server. If
the build fails, debug!
-123
View File
@@ -1,123 +0,0 @@
# ProtocolLib Agent Guide
This file applies to the entire repository. Read `.github/CONTRIBUTING.md` before changing Minecraft support, packet
definitions, reflection, or public wrappers.
## Repository Basics
- The main project uses the Gradle wrapper. Do not use `TinyProtocol/pom.xml` to build ProtocolLib.
- The Java toolchain and current Spigot dependency are defined in `build.gradle.kts`.
- Keep the Java 25 build toolchain separate from the Java 17 production bytecode target.
- Production code is under `src/main/java`; JUnit tests and NMS test initialization are under `src/test/java`.
- Keep changes focused. Do not combine protocol fixes with unrelated cleanup or formatting.
- Preserve public API and binary compatibility unless the task explicitly requires a breaking change.
## Build and Test Commands
```shell
./gradlew test
./gradlew build shadowJar
```
Run the smallest relevant test while developing:
```shell
./gradlew test --tests fully.qualified.TestClass
./gradlew test --tests fully.qualified.TestClass.testMethod
```
After changing production Java code, run the targeted tests and then the complete test suite. Use `build shadowJar` when
the task affects packaging, dependencies, release support, or the final plugin artifact.
Do not introduce another build system, test runner, formatter, or lint tool unless the repository adopts it separately.
## Important Code Areas
- `PacketType.java`: public packet constants, current IDs, aliases, and deprecated packet definitions.
- `injector/packet/internal/ProtocolRegistry_1_20_5.java`: current packet discovery and codec registration.
- `utility/MinecraftVersion.java`: named Minecraft versions and the latest supported release.
- `utility/MinecraftProtocolVersion.java`: Minecraft release to protocol-number mappings.
- `ProtocolLibrary.java`: supported version bounds and latest release date.
- `utility/MinecraftReflection.java`: centralized NMS and CraftBukkit class discovery.
- `reflect/StructureModifier.java`: indexed packet field access and conversion contract.
- `wrappers/`: stable public representations of unstable NMS structures.
- `events/PacketContainer.java` and `events/AbstractStructure.java`: packet construction and typed field access.
- `src/test/java/com/comphenix/protocol/BukkitInitialization.java`: test bootstrap for CraftBukkit and NMS access.
## Minecraft Release Updates
Follow the complete checklist in `.github/CONTRIBUTING.md`. In particular:
- Update all version metadata together; do not bump only the Gradle dependency.
- Reconcile every serverbound and clientbound packet in every protocol phase with `PacketType`.
- Add packet types for newly split packets instead of forcing incompatible shapes through an older packet constant.
- Preserve removed public `PacketType` constants in the deprecated section.
- Update `MinecraftProtocolVersion` only when the protocol number actually changed.
Every packet exposed by the current NMS protocol registries must have a matching `PacketType`. An undefined or incorrectly
renamed packet can abort registration of later packets in the same registry.
## Version Compatibility
- Put version checks at the narrowest affected boundary.
- Keep the pre-change code path intact whenever possible.
- Use named `MinecraftVersion` constants with `atOrAbove()` instead of scattered version strings.
- A public method should keep the same signature and broad contract across versions.
- If Minecraft removed a concept, return an unsupported or absent result consistent with existing APIs. Do not claim an
unrelated packet or class is the removed type.
- When Minecraft splits one packet into several packets, expose and use the new packet types explicitly.
- Preserve unrelated packet fields when adapting or rewriting one logical value.
The current test dependency exercises the latest supported server. Review older guarded paths carefully because they may
not execute in the local test run.
## Packet and Reflection Rules
- Treat the current server packet classes, record components, constructors, and codecs as authoritative.
- Prefer existing `MinecraftReflection`, `FuzzyReflection`, and accessor helpers over new direct reflection.
- Prefer structural and type-based matching where obfuscated names are unstable.
- Search for an existing class resolver, converter, or accessor before adding another one.
- Cache stable reflective lookups following nearby patterns; do not repeatedly scan classes on packet hot paths.
- Do not hide reflection failures with broad catches, silent defaults, or unrelated fallback types.
- Account for static fields, inherited fields, records, nested implementations, and duplicate field types when writing
fuzzy contracts.
## `StructureModifier` Contract
Typed accessors returned from packet APIs must behave like normal `StructureModifier` instances.
- `size()`, `getFields()`, `getField()`, target metadata, and field metadata must agree.
- `readSafely()` returns `null` for an absent index.
- `writeSafely()` ignores an absent index without throwing.
- `withTarget()` must preserve the modifier's behavior for the new target.
- `writeDefaults()` must remain safe.
- Converters must be applied symmetrically on reads and writes.
If a packet no longer contains a real backing field, prefer an empty modifier when compatible. If a virtual field is
required for API compatibility, initialize all modifier metadata and test its inherited methods.
## Wrapper and Converter Rules
- Wrapper factories, converters, getters, setters, and `deepClone()` must describe the same logical value.
- Preserve converter symmetry: converting specific to generic and back must not unexpectedly lose represented state.
- If a wrapper handle changes from a nested value to an entire packet, define logical `equals()` and `hashCode()` so
unrelated packet metadata does not affect equality.
- Clones that represent the same logical value should compare equal.
- Do not fabricate legacy states that the modern wire format cannot distinguish; document or surface the limitation.
- Record or immutable packet updates must retain entity IDs, flags, and other components not owned by the wrapper.
## Tests
- Call `BukkitInitialization.initializeAll()` before tests that resolve CraftBukkit or NMS classes.
- Prefer regression tests using the actual Spigot packet classes supplied by the test dependency.
- Test reads, writes, safe access, cloning, equality, and preservation of unrelated fields when changing wrappers.
- Test modifier metadata and inherited methods when introducing a custom `StructureModifier`.
- Re-enable disabled tests when fixing the incompatibility that caused them to be disabled.
- Do not weaken assertions or encode known incorrect behavior merely to make a new Minecraft version pass.
## Documentation and Review
- Update Javadocs when version support, nullability, or unsupported behavior changes.
- Update `.github/CONTRIBUTING.md` when the repository-wide build or release workflow changes.
- Keep this file operational and durable; do not add task-specific notes, temporary workarounds, or current PR details.
- Before finishing, inspect the complete diff for accidental API changes and unrelated edits.
-368
View File
@@ -1,368 +0,0 @@
# Packet Wrappers
Type-safe wrappers around ProtocolLib `PacketContainer` objects.
Each wrapper lives under `net.dmulloy2.protocol.wrappers`, organized by protocol phase then direction.
---
## Package layout
```
net.dmulloy2.protocol/
AbstractPacket.java # base class for all wrappers
wrappers/
game/
clientbound/
WrappedClientbound<PacketName>Packet.java
serverbound/
WrappedServerbound<PacketName>Packet.java
login/
clientbound/ …
serverbound/ …
status/
clientbound/ …
serverbound/ …
```
New NMS types that are reusable across multiple packets belong in the core ProtocolLib packages, not here:
| What | Where |
|---|---------------------------------------------------------------------------------------|
| New enum wrapper | inner enum + registration in `com.comphenix.protocol.wrappers.EnumWrappers |
| New structure wrapper | `com.comphenix.protocol.wrappers.Wrapped<Name>.java` + getter in `AbstractStructure` |
| Packet wrapper | `net.dmulloy2.protocol.wrappers.<phase>.<direction>.Wrapped<NMSPacketName>` |
---
## Class Structure
* Every wrapper extends `AbstractPacket`
* There should be 3 constructors: no-args (create new packet with default values), all-args (create new packet with specified values), and from-container (wrap existing packet)
* Then create getters and setters for each field in the packet, using the appropriate `StructureModifier` methods from `AbstractStructure`.
```java
package net.dmulloy2.protocol.wrappers.game.serverbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.PacketConstructor;
import net.dmulloy2.protocol.AbstractPacket;
public class WrappedServerboundAcceptTeleportationPacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Play.Client.TELEPORT_ACCEPT;
public WrappedServerboundAcceptTeleportationPacket() {
super(new PacketContainer(TYPE), TYPE);
handle.getModifier().writeDefaults();
}
public WrappedServerboundAcceptTeleportationPacket(int id) {
this(PacketConstructor.DEFAULT.withPacket(TYPE, new Class<?>[] { int.class }).createPacket(id));
}
public WrappedServerboundAcceptTeleportationPacket(PacketContainer packet) {
super(packet, TYPE);
}
// getters and setters for packet fields
}
```
`AbstractPacket` validates the type and exposes `getHandle()`, `sendPacket(Player)`, and `receivePacket(Player)`.
---
## Reading and writing packet fields
Always go through `AbstractStructure`'s typed getters. Never touch `getModifier()` directly or read raw field indices without a type-checked accessor.
### Primitives
```java
handle.getIntegers().read(0) // int
handle.getIntegers().write(0, value)
handle.getBooleans().read(0) // boolean
handle.getBooleans().write(0, value)
handle.getFloat().read(0) // float (note: singular, not getFloats)
handle.getFloat().write(0, value)
handle.getLongs() / getDoubles() / getBytes() / getShorts() / getStrings() …
```
Field indices within a type are zero-based and ordered by their declaration order in the NMS class.
### Bukkit / ProtocolLib wrapper types
```java
handle.getVectors().read(0) // org.bukkit.util.Vector ↔ Vec3
handle.getPositionMoveRotations().read(0) // WrappedPositionMoveRotation ↔ PositionMoveRotation
handle.getEntityModifier(world).read(0) // org.bukkit.entity.Entity
handle.getItemModifier().read(0) // org.bukkit.inventory.ItemStack
handle.getBlockPositionModifier().read(0) // BlockPosition
handle.getNbtModifier().read(0) // NbtBase<?>
handle.getDataWatcherModifier().read(0) // WrappedDataWatcher
// …see AbstractStructure for the full list
```
### Enums
Use `EnumWrappers.get<Name>Converter()` with the appropriate accessor:
```java
// Single enum field
handle.getEnumModifier(EnumWrappers.Hand.class, EnumWrappers.getHandClass()).read(0)
// Set of enum values (e.g. RelativeArgument)
handle.getSets(EnumWrappers.getRelativeArgumentConverter()).read(0)
handle.getSets(EnumWrappers.getRelativeArgumentConverter()).write(0, set)
```
### Nested structures (InternalStructure)
Only use `getStructures()` when you genuinely need to inspect a sub-structure that has no dedicated typed wrapper yet. Once you have written a `Wrapped<Name>` class, add a getter to `AbstractStructure` and use that instead.
---
## Adding a new NMS enum wrapper
1. **Declare** the enum as an inner class of `EnumWrappers`, matching NMS constant names exactly:
```java
// EnumWrappers.java
public enum RelativeArgument {
X, Y, Z, Y_ROT, X_ROT, DELTA_X, DELTA_Y, DELTA_Z, ROTATE_DELTA
}
```
2. **Register** the NMS class, class getter, converter, and `associate()` call — all following the same pattern as `Hand`, `Direction`, etc.:
```java
// static field
private static Class<?> RELATIVE_CLASS = null;
// inside initialize() — class loading
RELATIVE_CLASS = MinecraftReflection.getMinecraftClass("world.entity.Relative");
// inside initialize() — association
associate(RELATIVE_CLASS, RelativeArgument.class, getRelativeArgumentConverter());
// public API
public static Class<?> getRelativeArgumentClass() {
initialize();
return RELATIVE_CLASS;
}
public static EquivalentConverter<RelativeArgument> getRelativeArgumentConverter() {
return new EnumConverter<>(getRelativeArgumentClass(), RelativeArgument.class);
}
```
`EnumConverter` maps by constant name (`Enum.valueOf`). Use it whenever the NMS class **is** a real Java enum and the constant names match. Use `FauxEnumConverter` only for NMS classes that are enum-like but not `java.lang.Enum`.
---
## Adding a new NMS structure wrapper
`PositionMoveRotation` is the reference example.
### 1 — Create `com.comphenix.protocol.wrappers.Wrapped<Name>.java`
```java
public class WrappedPositionMoveRotation {
// mutable fields matching the NMS record
/** Read from a live InternalStructure using AbstractStructure typed getters. */
public static WrappedPositionMoveRotation fromHandle(InternalStructure handle) {
WrappedPositionMoveRotation w = new WrappedPositionMoveRotation();
Vector pos = handle.getVectors().read(0);
Vector delta = handle.getVectors().read(1);
w.x = pos.getX(); w.y = pos.getY(); w.z = pos.getZ();
w.deltaX = delta.getX(); …
w.yaw = handle.getFloat().read(0);
w.pitch = handle.getFloat().read(1);
return w;
}
/**
* Returns an EquivalentConverter for use in StructureModifier.withType().
*
* getSpecific — delegates to fromHandle(InternalStructure)
* getGeneric — constructs a new NMS record via its public constructor
*/
public static EquivalentConverter<WrappedPositionMoveRotation> getConverter() {
return new EquivalentConverter<>() {
@Override
public WrappedPositionMoveRotation getSpecific(Object generic) {
return fromHandle(InternalStructure.getConverter().getSpecific(generic));
}
@Override
public Object getGeneric(WrappedPositionMoveRotation s) {
try {
Class<?> vec3 = MinecraftReflection.getVec3DClass();
Class<?> pmr = MinecraftReflection.getMinecraftClass("world.entity.PositionMoveRotation");
Constructor<?> v = vec3.getDeclaredConstructor(double.class, double.class, double.class);
Constructor<?> p = pmr.getDeclaredConstructor(vec3, vec3, float.class, float.class);
return p.newInstance(v.newInstance(s.x, s.y, s.z),
v.newInstance(s.deltaX, s.deltaY, s.deltaZ),
s.yaw, s.pitch);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
@Override public Class<WrappedPositionMoveRotation> getSpecificType() {
return WrappedPositionMoveRotation.class;
}
};
}
}
```
**Reading** always goes through `AbstractStructure` getters (`getVectors()`, `getFloat()`, …) — never raw field accessors.
**Writing** must construct a fresh NMS instance when the NMS type is an immutable record (final fields). Mutable NMS classes can be modified via their own `InternalStructure` instead.
### 2 — Add a getter to `AbstractStructure`
```java
// AbstractStructure.java — alongside getVectors(), getIntegers(), etc.
public StructureModifier<WrappedPositionMoveRotation> getPositionMoveRotations() {
return structureModifier.withType(
MinecraftReflection.getMinecraftClass("world.entity.PositionMoveRotation"),
WrappedPositionMoveRotation.getConverter());
}
```
### 3 — Use it in the packet wrapper
```java
public WrappedPositionMoveRotation getChange() {
return handle.getPositionMoveRotations().read(0);
}
public void setChange(WrappedPositionMoveRotation change) {
handle.getPositionMoveRotations().write(0, change);
}
```
---
## Unit tests
Each wrapper class gets its own test file named `<WrapperClassName>Test.java`, co-located with the wrapper under `src/test/java/net/dmulloy2/protocol/wrappers/<phase>/<direction>/`. Do not combine tests for multiple wrappers in a single file.
Every test class must call `BukkitInitialization.initializeAll()` in a `@BeforeAll` method — this bootstraps the Minecraft registry and mocked Bukkit server required for `PacketContainer` and NMS reflection to work.
Cover these scenarios for each wrapper:
* testAllArgsCreate
* Construct a fresh wrapper using the all-args constructor with known test values
* Get the NMS handle packet via `getHandle()`
* Assert that all fields in the NMS handle match the known test values
* testNoArgsCreate
* Construct a fresh wrapper using the no-args constructor
* Get the NMS handle packet via `getHandle()`
* Assert that all fields in the NMS handle are not null
* testModifyExistingPacket
* Create the NMS handle packet directly from its constructor, passing known test values
* Create a packet container from the NMS handle via `PacketContainer.fromPacket()`
* Construct the wrapper using the packet container
* Assert that all fields in the wrapper match the known test values
* Change all fields in the wrapper to new test values
* Verify that the NMS handle packet reflects the new test values
* testWrongPacketTypeThrows
* Create a packet container for a different packet type
* Attempt to construct the wrapper using this incorrect packet container
* Assert that an appropriate exception is thrown (e.g. IllegalArgumentException)
### Appropriate test values
* For numeric fields, use a mix of positive, negative, and zero values (e.g. -5, 0, 3)
* For enum fields, prefer the 2nd or 3rd constant rather than the first, to ensure the wrapper correctly handles non-default enum values
* Additionally, if the enum constant is @Deprecated, do not use it
Here's a good example of a complete test class for `WrappedClientboundSetExperiencePacket`:
```java
class WrappedClientboundSetExperiencePacketTest {
@BeforeAll
static void beforeAll() {
BukkitInitialization.initializeAll();
}
@Test
void testAllArgsCreate() {
WrappedClientboundSetExperiencePacket w = new WrappedClientboundSetExperiencePacket(0.75f, 350, 12);
assertEquals(PacketType.Play.Server.EXPERIENCE, w.getHandle().getType());
ClientboundSetExperiencePacket p = (ClientboundSetExperiencePacket) w.getHandle().getHandle();
assertEquals(0.75f, p.getExperienceProgress(), 1e-4f);
assertEquals(350, p.getTotalExperience());
assertEquals(12, p.getExperienceLevel());
}
@Test
void testNoArgsCreate() {
WrappedClientboundSetExperiencePacket w = new WrappedClientboundSetExperiencePacket();
assertEquals(PacketType.Play.Server.EXPERIENCE, w.getHandle().getType());
ClientboundSetExperiencePacket p = (ClientboundSetExperiencePacket) w.getHandle().getHandle();
assertEquals(0.0f, p.getExperienceProgress(), 1e-4f);
assertEquals(0, p.getTotalExperience());
assertEquals(0, p.getExperienceLevel());
}
@Test
void testModifyExistingPacket() {
ClientboundSetExperiencePacket nmsPacket = new ClientboundSetExperiencePacket(
0.5f, 100, 5
);
PacketContainer container = PacketContainer.fromPacket(nmsPacket);
WrappedClientboundSetExperiencePacket wrapper = new WrappedClientboundSetExperiencePacket(container);
assertEquals(0.5f, wrapper.getExperienceProgress(), 1e-4f);
assertEquals(100, wrapper.getTotalExperience());
assertEquals(5, wrapper.getExperienceLevel());
wrapper.setExperienceProgress(0.25f);
wrapper.setTotalExperience(200);
wrapper.setExperienceLevel(10);
assertEquals(0.25f, nmsPacket.getExperienceProgress(), 1e-4f);
assertEquals(200, nmsPacket.getTotalExperience());
assertEquals(10, nmsPacket.getExperienceLevel());
}
@Test
void testModifyExistingPacket() {
ClientboundSetExperiencePacket nmsPacket = new ClientboundSetExperiencePacket(
0.5f, 100, 5
);
PacketContainer container = PacketContainer.fromPacket(nmsPacket);
WrappedClientboundSetExperiencePacket wrapper = new WrappedClientboundSetExperiencePacket(container);
wrapper.setTotalExperience(200);
assertEquals(0.5f, wrapper.getExperienceProgress(), 1e-4f);
assertEquals(200, wrapper.getTotalExperience());
assertEquals(5, wrapper.getExperienceLevel());
}
@Test
void testWrongPacketTypeThrows() {
assertThrows(IllegalArgumentException.class,
() -> new WrappedClientboundSetExperiencePacket(
new PacketContainer(PacketType.Play.Server.CHAT)));
}
}
```
@@ -64,7 +64,7 @@ public abstract class TinyProtocol {
// Packets we have to intercept
private static final Class<?> PACKET_LOGIN_IN_START = Reflection.getClass("{nms}.PacketLoginInStart", "net.minecraft.network.protocol.login.PacketLoginInStart");
private static final FieldAccessor<String> getPlayerName = new PlayerNameAccessor();
private static final FieldAccessor<GameProfile> getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START, GameProfile.class, 0);
// Speedup channel lookup
private Map<String, Channel> channelLookup = new MapMaker().weakValues().makeMap();
@@ -492,59 +492,6 @@ public abstract class TinyProtocol {
}
}
/**
* Get the player name from the login start packet.
* This fixes the issue from 1.19 where the GameProfile field has been removed from this login packet.
*
* @author gamerover98
*/
private static class PlayerNameAccessor implements FieldAccessor<String> {
private FieldAccessor<String> getPlayerName;
private FieldAccessor<GameProfile> getGameProfile;
PlayerNameAccessor() {
try {
this.getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START, GameProfile.class, 0);
} catch (IllegalArgumentException illegalArgumentException) {
// nothing to do.
}
try {
//HOT-FIX for 1.19+
this.getPlayerName = Reflection.getField(PACKET_LOGIN_IN_START, String.class, 0);
} catch (IllegalArgumentException illegalArgumentException) {
// nothing to do.
}
if (getGameProfile == null && getPlayerName == null) {
throw new UnsupportedOperationException("The current server version is not supported by TinyProtocol");
}
}
@Override
public String get(Object target) {
if (getPlayerName != null) {
String playerName = getPlayerName.get(target);
return playerName.substring(0, Math.min(16, playerName.length()));
}
return getGameProfile.get(target).getName();
}
@Override
public void set(Object target, Object value) {
throw new UnsupportedOperationException("Not supported");
}
@Override
public boolean hasField(Object target) {
return getPlayerName != null
? getPlayerName.hasField(target)
: getGameProfile.hasField(target);
}
}
/**
* Channel handler that is inserted into the player's channel pipeline, allowing us to intercept sent and received packets.
*
@@ -586,7 +533,8 @@ public abstract class TinyProtocol {
private void handleLoginStart(Channel channel, Object packet) {
if (PACKET_LOGIN_IN_START.isInstance(packet)) {
channelLookup.put(getPlayerName.get(packet), channel);
GameProfile profile = getGameProfile.get(packet);
channelLookup.put(profile.getName(), channel);
}
}
}
+47 -40
View File
@@ -1,23 +1,38 @@
//import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
//import io.github.patrick.gradle.remapper.RemapTask
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.shadowJar
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
plugins {
`java-library`
`maven-publish`
signing
id("com.gradleup.shadow") version "9.4.0"
id("io.github.patrick.remapper") version "1.4.3"
id("com.vanniktech.maven.publish") version "0.36.0"
`signing`
id("com.gradleup.shadow") version "9.0.2"
// id("io.github.patrick.remapper") version "1.4.2"
id("com.vanniktech.maven.publish") version "0.34.0"
id("io.papermc.paperweight.userdev") version "2.0.0-beta.18"
}
group = "net.dmulloy2"
description = "Provides access to the Minecraft protocol"
val mcVersion = "26.2"
val mcVersion = "1.21.8"
val isSnapshot = version.toString().endsWith("-SNAPSHOT")
val isJitPack = System.getenv("JITPACK")?.equals("true", ignoreCase = true) ?: false
val commitHash = System.getenv("COMMIT_SHA") ?: ""
val isCI = commitHash.isNotEmpty()
paperweight {
reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArtifactConfiguration.REOBF_PRODUCTION
}
tasks.assemble {
dependsOn(tasks.reobfJar)
}
/*tasks.shadowJar {
dependsOn(tasks.assemble)
}*/
repositories {
if (!isCI) {
mavenLocal()
@@ -44,28 +59,31 @@ repositories {
}
dependencies {
implementation("net.bytebuddy:byte-buddy:1.18.2")
compileOnly("org.spigotmc:spigot-api:${mcVersion}-R0.1-SNAPSHOT")
compileOnly("org.spigotmc:spigot:${mcVersion}-R0.1-SNAPSHOT")//:remapped-mojang")
compileOnly("io.netty:netty-all:4.2.8.Final")
compileOnly("net.kyori:adventure-text-serializer-gson:4.25.0")
implementation("net.bytebuddy:byte-buddy:1.17.5")
// compileOnly("org.spigotmc:spigot-api:${mcVersion}-R0.1-SNAPSHOT")
// compileOnly("org.spigotmc:spigot:${mcVersion}-R0.1-SNAPSHOT:remapped-mojang")
compileOnly("io.netty:netty-all:4.0.23.Final")
compileOnly("net.kyori:adventure-text-serializer-gson:4.24.0")
compileOnly("net.kyori:adventure-text-serializer-ansi:4.24.0")
compileOnly("com.googlecode.json-simple:json-simple:1.1.1")
testImplementation("org.junit.jupiter:junit-jupiter-engine:6.0.1")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:6.0.1")
testRuntimeOnly("org.junit.platform:junit-platform-launcher:6.0.1")
testImplementation("org.mockito:mockito-core:5.21.0")
testImplementation("io.netty:netty-common:4.2.8.Final")
testImplementation("io.netty:netty-transport:4.2.8.Final")
testImplementation("org.spigotmc:spigot:${mcVersion}-R0.1-SNAPSHOT")//:remapped-mojang")
testImplementation("net.kyori:adventure-text-serializer-gson:4.25.0")
testImplementation("net.kyori:adventure-text-serializer-plain:4.25.0")
paperweight.paperDevBundle("1.21.8-R0.1-SNAPSHOT")
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.0")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.10.0")
testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.10.0")
testImplementation("org.mockito:mockito-core:5.6.0")
testImplementation("io.netty:netty-common:4.1.97.Final")
testImplementation("io.netty:netty-transport:4.1.97.Final")
// testImplementation("org.spigotmc:spigot:${mcVersion}-R0.1-SNAPSHOT:remapped-mojang")
testImplementation("net.kyori:adventure-text-serializer-gson:4.24.0")
testImplementation("net.kyori:adventure-text-serializer-plain:4.24.0")
testImplementation("net.kyori:adventure-text-serializer-ansi:4.24.0")
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(25))
}
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
tasks {
@@ -90,42 +108,31 @@ tasks {
}
relocate("net.bytebuddy", "com.comphenix.net.bytebuddy")
manifest {
/*manifest {
attributes(
"paperweight-mappings-namespace" to "mojang"
)
}
}*/
archiveFileName = "ProtocolLib.jar"
// archiveFileName = "ProtocolLib.jar"
}
/*remap {
dependsOn("shadowJar")
inputTask.set(getByName<ShadowJar>("shadowJar"))
version.set(mcVersion)
action.set(RemapTask.Action.MOJANG_TO_SPIGOT)
reobfJar {
outputJar = layout.buildDirectory.file("libs/ProtocolLib.jar")
}
assemble {
dependsOn("remap")
}*/
javadoc {
options.encoding = "UTF-8"
}
compileJava {
options.encoding = "UTF-8"
options.release.set(17)
}
}
mavenPublishing {
publishToMavenCentral()
if (!isSnapshot && !isJitPack) {
signAllPublications()
}
signAllPublications()
coordinates("$group", project.name, "$version")
+1 -1
View File
@@ -1,4 +1,4 @@
version=5.5.0-SNAPSHOT
version=5.4.1-SNAPSHOT
mavenCentralUsername=
mavenCentralPassword=
signing.keyId=
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+8 -7
View File
@@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright © 2015 the original authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,8 +15,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@@ -57,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -86,7 +84,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
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
@@ -114,6 +112,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -171,6 +170,7 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -203,14 +203,15 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# * 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" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
Vendored
+12 -13
View File
@@ -13,8 +13,6 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@@ -45,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
@@ -59,21 +57,22 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
@@ -0,0 +1,38 @@
package com.comphenix.protocol;
import com.comphenix.protocol.injector.PacketFilterManager;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.server.PluginDisableEvent;
class BukkitListener implements Listener {
private final PacketFilterManager manager;
BukkitListener(PacketFilterManager manager) {
this.manager = manager;
}
// @EventHandler(priority = EventPriority.LOWEST)
// public void handleLogin(PlayerLoginEvent event) {
// networkManagerInjector.getInjector(event.getPlayer()).inject();
// }
@EventHandler(priority = EventPriority.LOWEST)
public void handleJoin(PlayerJoinEvent event) {
manager.injectPlayer(event.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR)
public void handleQuit(PlayerQuitEvent event) {
manager.removePlayer(event.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR)
public void handlePluginUnload(PluginDisableEvent event) {
manager.removePacketListeners(event.getPlugin());
}
}
@@ -21,21 +21,19 @@ import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
/**
* Base class for all our commands.
*
* @author Kristian
*/
abstract class CommandBase implements CommandExecutor {
abstract class CommandBase {
public static final ReportType REPORT_COMMAND_ERROR = new ReportType("Cannot execute command %s.");
public static final ReportType REPORT_UNEXPECTED_COMMAND = new ReportType("Incorrect command assigned to %s.");
@@ -58,15 +56,8 @@ abstract class CommandBase implements CommandExecutor {
this.minimumArgumentCount = minimumArgumentCount;
}
@Override
public final boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
public boolean onCommand(CommandSender sender, String[] args) {
try {
// Make sure we're dealing with the correct command
if (!command.getName().equalsIgnoreCase(name)) {
reporter.reportWarning(this, Report.newBuilder(REPORT_UNEXPECTED_COMMAND).messageParam(this));
return false;
}
if (permission != null && !sender.hasPermission(permission)) {
sender.sendMessage(ChatColor.RED + "You haven't got permission to run this command.");
return true;
@@ -81,7 +72,7 @@ abstract class CommandBase implements CommandExecutor {
}
} catch (Throwable ex) {
reporter.reportDetailed(this,
Report.newBuilder(REPORT_COMMAND_ERROR).error(ex).messageParam(name).callerParam(sender, label, args)
Report.newBuilder(REPORT_COMMAND_ERROR).error(ex).messageParam(name).callerParam(sender, name, args)
);
return true;
}
@@ -0,0 +1,22 @@
package com.comphenix.protocol;
import com.comphenix.protocol.injector.PacketFilterManager;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLoginEvent;
class LegacyLoginListener implements Listener {
private final PacketFilterManager manager;
LegacyLoginListener(PacketFilterManager manager) {
this.manager = manager;
}
@SuppressWarnings("deprecation")
@EventHandler(priority = EventPriority.LOWEST)
public void handleLogin(PlayerLoginEvent event) {
manager.injectPlayer(event.getPlayer());
}
}
@@ -16,22 +16,6 @@
*/
package com.comphenix.protocol;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
@@ -48,11 +32,28 @@ import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.events.ListeningWhitelist;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.PacketFilterManager;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.Plugin;
/**
* Logs packets to a given stream
* @author dmulloy2
*/
public class PacketLogging implements CommandExecutor, PacketListener {
public class PacketLogging extends CommandBase implements PacketListener {
public static final String NAME = "packetlog";
private static MethodAccessor HEX_DUMP;
@@ -69,93 +70,94 @@ public class PacketLogging implements CommandExecutor, PacketListener {
private final ProtocolManager manager;
private final Plugin plugin;
PacketLogging(Plugin plugin, ProtocolManager manager) {
PacketLogging(ErrorReporter reporter, Plugin plugin, PacketFilterManager manager) {
super(reporter, CommandBase.PERMISSION_ADMIN, NAME, 2);
this.plugin = plugin;
this.manager = manager;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
public boolean handleCommand(CommandSender sender, String[] args) {
PacketType type = null;
try {
if (args.length > 2) {
Protocol protocol;
if (args.length <= 2) {
sender.sendMessage(ChatColor.RED + "Invalid syntax: /packetlog <protocol> <sender> <packet> [location]");
return true;
}
try {
protocol = Protocol.valueOf(args[0].toUpperCase());
} catch (IllegalArgumentException ex) {
sender.sendMessage(ChatColor.RED + "Unknown protocol " + args[0]);
return true;
}
Protocol protocol;
Sender pSender;
try {
protocol = Protocol.valueOf(args[0].toUpperCase());
} catch (IllegalArgumentException ex) {
sender.sendMessage(ChatColor.RED + "Unknown protocol " + args[0]);
return true;
}
try {
pSender = Sender.valueOf(args[1].toUpperCase());
} catch (IllegalArgumentException ex) {
sender.sendMessage(ChatColor.RED + "Unknown sender: " + args[1]);
return true;
}
Sender pSender;
try {
try { // Try IDs first
int id = Integer.parseInt(args[2]);
type = PacketType.findCurrent(protocol, pSender, id);
} catch (NumberFormatException ex) { // Check packet names
String name = args[2];
for (PacketType packet : PacketType.values()) {
if (packet.getProtocol() == protocol && packet.getSender() == pSender) {
if (packet.name().equalsIgnoreCase(name)) {
try {
pSender = Sender.valueOf(args[1].toUpperCase());
} catch (IllegalArgumentException ex) {
sender.sendMessage(ChatColor.RED + "Unknown sender: " + args[1]);
return true;
}
try {
try { // Try IDs first
int id = Integer.parseInt(args[2]);
type = PacketType.findCurrent(protocol, pSender, id);
} catch (NumberFormatException ex) { // Check packet names
String name = args[2];
for (PacketType packet : PacketType.values()) {
if (packet.getProtocol() == protocol && packet.getSender() == pSender) {
if (packet.name().equalsIgnoreCase(name)) {
type = packet;
break;
}
for (String className : packet.getClassNames()) {
if (className.equalsIgnoreCase(name)) {
type = packet;
break;
}
for (String className : packet.getClassNames()) {
if (className.equalsIgnoreCase(name)) {
type = packet;
break;
}
}
}
}
}
} catch (IllegalArgumentException ex) { // RIP
type = null;
}
} catch (IllegalArgumentException ex) { // RIP
type = null;
}
if (type == null) {
sender.sendMessage(ChatColor.RED + "Unknown packet: " + args[2]);
return true;
}
if (args.length > 3) {
if (args[3].equalsIgnoreCase("console")) {
this.location = LogLocation.CONSOLE;
} else {
this.location = LogLocation.FILE;
}
}
if (pSender == Sender.CLIENT) {
if (receivingTypes.contains(type)) {
receivingTypes.remove(type);
} else {
receivingTypes.add(type);
}
} else {
if (sendingTypes.contains(type)) {
sendingTypes.remove(type);
} else {
sendingTypes.add(type);
}
}
startLogging();
sender.sendMessage(ChatColor.GREEN + "Now logging " + type.getPacketClass().getSimpleName());
if (type == null) {
sender.sendMessage(ChatColor.RED + "Unknown packet: " + args[2]);
return true;
}
sender.sendMessage(ChatColor.RED + "Invalid syntax: /packetlog <protocol> <sender> <packet> [location]");
if (args.length > 3) {
if (args[3].equalsIgnoreCase("console")) {
this.location = LogLocation.CONSOLE;
} else {
this.location = LogLocation.FILE;
}
}
if (pSender == Sender.CLIENT) {
if (receivingTypes.contains(type)) {
receivingTypes.remove(type);
} else {
receivingTypes.add(type);
}
} else {
if (sendingTypes.contains(type)) {
sendingTypes.remove(type);
} else {
sendingTypes.add(type);
}
}
startLogging();
sender.sendMessage(ChatColor.GREEN + "Now logging " + type.getPacketClass().getSimpleName());
return true;
} catch (Throwable ex) {
sender.sendMessage(ChatColor.RED + "Failed to parse command: " + ex);
@@ -14,7 +14,7 @@ import java.util.Map;
import java.util.UUID;
import java.util.function.BiConsumer;
import org.apache.commons.lang.WordUtils;
import com.comphenix.protocol.utility.WordUtils;
import org.bukkit.Bukkit;
import com.comphenix.protocol.PacketTypeLookup.ClassLookup;
@@ -133,121 +133,114 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType CUSTOM_CHAT_COMPLETIONS = new PacketType(PROTOCOL, SENDER, 0x17, "CustomChatCompletions");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x18, "CustomPayload", "SPacketCustomPayload");
public static final PacketType DAMAGE_EVENT = new PacketType(PROTOCOL, SENDER, 0x19, "DamageEvent", "ClientboundDamageEventPacket");
public static final PacketType DEBUG_BLOCK_VALUE = new PacketType(PROTOCOL, SENDER, 0x1A, "DebugBlockValue");
public static final PacketType DEBUG_CHUNK_VALUE = new PacketType(PROTOCOL, SENDER, 0x1B, "DebugChunkValue");
public static final PacketType DEBUG_ENTITY_VALUE = new PacketType(PROTOCOL, SENDER, 0x1C, "DebugEntityValue");
public static final PacketType DEBUG_EVENT = new PacketType(PROTOCOL, SENDER, 0x1D, "DebugEvent");
public static final PacketType DEBUG_SAMPLE = new PacketType(PROTOCOL, SENDER, 0x1E, "DebugSample");
public static final PacketType DELETE_CHAT_MESSAGE = new PacketType(PROTOCOL, SENDER, 0x1F, "DeleteChat");
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x20, "Disconnect", "KickDisconnect", "SPacketDisconnect");
public static final PacketType DISGUISED_CHAT = new PacketType(PROTOCOL, SENDER, 0x21, "DisguisedChat");
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x22, "EntityEvent", "EntityStatus", "SPacketEntityStatus");
public static final PacketType ENTITY_POSITION_SYNC = new PacketType(PROTOCOL, SENDER, 0x23, "EntityPositionSync");
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x24, "Explode", "Explosion", "SPacketExplosion");
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x25, "ForgetLevelChunk", "UnloadChunk", "SPacketUnloadChunk");
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x26, "GameEvent", "GameStateChange", "SPacketChangeGameState");
public static final PacketType GAME_RULE_VALUES = new PacketType(PROTOCOL, SENDER, 0x27, "GameRuleValues");
public static final PacketType GAME_TEST_HIGHLIGHT_POS = new PacketType(PROTOCOL, SENDER, 0x28, "GameTestHighlightPos");
public static final PacketType OPEN_WINDOW_HORSE = new PacketType(PROTOCOL, SENDER, 0x29, "MountScreenOpen", "HorseScreenOpen", "OpenWindowHorse");
public static final PacketType HURT_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x2A, "HurtAnimation", "ClientboundHurtAnimationPacket");
public static final PacketType INITIALIZE_BORDER = new PacketType(PROTOCOL, SENDER, 0x2B, "InitializeBorder");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x2C, "KeepAlive", "SPacketKeepAlive");
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x2D, "LevelChunkWithLight", "MapChunk", "SPacketChunkData");
public static final PacketType WORLD_EVENT = new PacketType(PROTOCOL, SENDER, 0x2E, "LevelEvent", "WorldEvent", "SPacketEffect");
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x2F, "LevelParticles", "WorldParticles", "SPacketParticles");
public static final PacketType LIGHT_UPDATE = new PacketType(PROTOCOL, SENDER, 0x30, "LightUpdate");
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x31, "Login", "SPacketJoinGame");
public static final PacketType LOW_DISK_SPACE_WARNING = new PacketType(PROTOCOL, SENDER, 0x32, "LowDiskSpaceWarning");
public static final PacketType MAP = new PacketType(PROTOCOL, SENDER, 0x33, "MapItemData", "Map", "SPacketMaps");
public static final PacketType OPEN_WINDOW_MERCHANT = new PacketType(PROTOCOL, SENDER, 0x34, "MerchantOffers", "OpenWindowMerchant");
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x35, "net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Pos", "Entity$RelEntityMove", "Entity$PacketPlayOutRelEntityMove");
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x36, "net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$PosRot", "Entity$RelEntityMoveLook", "Entity$PacketPlayOutRelEntityMoveLook");
public static final PacketType MOVE_MINECART = new PacketType(PROTOCOL, SENDER, 0x37, "MoveMinecart");
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x38, "net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Rot", "Entity$EntityLook", "Entity$PacketPlayOutEntityLook");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x39, "MoveVehicle", "VehicleMove", "SPacketMoveVehicle");
public static final PacketType OPEN_BOOK = new PacketType(PROTOCOL, SENDER, 0x3A, "OpenBook");
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x3B, "OpenScreen", "OpenWindow", "SPacketOpenWindow");
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x3C, "OpenSignEditor", "SPacketSignEditorOpen");
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x3D, "Ping");
public static final PacketType PONG_RESPONSE = new PacketType(PROTOCOL, SENDER, 0x3E, "PongResponse");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x3F, "PlaceGhostRecipe", "AutoRecipe", "SPacketPlaceGhostRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x40, "PlayerAbilities", "Abilities", "SPacketPlayerAbilities");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x41, "PlayerChat", "Chat", "SPacketChat");
public static final PacketType PLAYER_COMBAT_END = new PacketType(PROTOCOL, SENDER, 0x42, "PlayerCombatEnd");
public static final PacketType PLAYER_COMBAT_ENTER = new PacketType(PROTOCOL, SENDER, 0x43, "PlayerCombatEnter");
public static final PacketType PLAYER_COMBAT_KILL = new PacketType(PROTOCOL, SENDER, 0x44, "PlayerCombatKill");
public static final PacketType PLAYER_INFO_REMOVE = new PacketType(PROTOCOL, SENDER, 0x45, "PlayerInfoRemove");
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x46, "PlayerInfoUpdate", "PlayerInfo");
public static final PacketType LOOK_AT = new PacketType(PROTOCOL, SENDER, 0x47, "PlayerLookAt", "LookAt", "SPacketPlayerPosLook");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x48, "PlayerPosition", "Position");
public static final PacketType PLAYER_ROTATION = new PacketType(PROTOCOL, SENDER, 0x49, "PlayerRotation");
public static final PacketType RECIPE_BOOK_ADD = new PacketType(PROTOCOL, SENDER, 0x4A, "RecipeBookAdd");
public static final PacketType RECIPE_BOOK_REMOVE = new PacketType(PROTOCOL, SENDER, 0x4B, "RecipeBookRemove");
public static final PacketType RECIPE_BOOK_SETTINGS = new PacketType(PROTOCOL, SENDER, 0x4C, "RecipeBookSettings");
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x4D, "RemoveEntities", "EntityDestroy", "SPacketDestroyEntities");
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x4E, "RemoveMobEffect", "RemoveEntityEffect", "SPacketRemoveEntityEffect");
public static final PacketType RESET_SCORE = new PacketType(PROTOCOL, SENDER, 0x4F, "ResetScore", "ResetScorePacket");
public static final PacketType REMOVE_RESOURCE_PACK = new PacketType(PROTOCOL, SENDER, 0x50, "ResourcePackPop", "ResourcePackPopPacket");
public static final PacketType ADD_RESOURCE_PACK = new PacketType(PROTOCOL, SENDER, 0x51, "ResourcePackPush", "ResourcePackPushPacket");
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x52, "Respawn", "SPacketRespawn");
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x53, "RotateHead", "EntityHeadRotation", "SPacketEntityHeadLook");
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x54, "SectionBlocksUpdate", "MultiBlockChange", "SPacketMultiBlockChange");
public static final PacketType SELECT_ADVANCEMENT_TAB = new PacketType(PROTOCOL, SENDER, 0x55, "SelectAdvancementsTab", "SelectAdvancementTab", "SPacketSelectAdvancementsTab");
public static final PacketType SERVER_DATA = new PacketType(PROTOCOL, SENDER, 0x56, "ServerData");
public static final PacketType SET_ACTION_BAR_TEXT = new PacketType(PROTOCOL, SENDER, 0x57, "SetActionBarText");
public static final PacketType SET_BORDER_CENTER = new PacketType(PROTOCOL, SENDER, 0x58, "SetBorderCenter");
public static final PacketType SET_BORDER_LERP_SIZE = new PacketType(PROTOCOL, SENDER, 0x59, "SetBorderLerpSize");
public static final PacketType SET_BORDER_SIZE = new PacketType(PROTOCOL, SENDER, 0x5A, "SetBorderSize");
public static final PacketType SET_BORDER_WARNING_DELAY = new PacketType(PROTOCOL, SENDER, 0x5B, "SetBorderWarningDelay");
public static final PacketType SET_BORDER_WARNING_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x5C, "SetBorderWarningDistance");
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x5D, "SetCamera", "Camera", "SPacketCamera");
public static final PacketType VIEW_CENTRE = new PacketType(PROTOCOL, SENDER, 0x5E, "SetChunkCacheCenter", "ViewCentre");
public static final PacketType VIEW_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x5F, "SetChunkCacheRadius", "ViewDistance");
public static final PacketType SET_CURSOR_ITEM = new PacketType(PROTOCOL, SENDER, 0x60, "SetCursorItem");
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x61, "SetDefaultSpawnPosition", "SpawnPosition", "SPacketSpawnPosition");
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x62, "SetDisplayObjective", "ScoreboardDisplayObjective", "SPacketDisplayObjective");
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x63, "SetEntityData", "EntityMetadata", "SPacketEntityMetadata");
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x64, "SetEntityLink", "AttachEntity", "SPacketEntityAttach");
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x65, "SetEntityMotion", "EntityVelocity", "SPacketEntityVelocity");
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x66, "SetEquipment", "EntityEquipment", "SPacketEntityEquipment");
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x67, "SetExperience", "Experience", "SPacketSetExperience");
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x68, "SetHealth", "UpdateHealth", "SPacketUpdateHealth");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x69, "SetHeldSlot", "SetCarriedItem", "HeldItemSlot", "SPacketHeldItemChange");
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x6A, "SetObjective", "ScoreboardObjective", "SPacketScoreboardObjective");
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x6B, "SetPassengers", "Mount", "SPacketSetPassengers");
public static final PacketType SET_PLAYER_INVENTORY = new PacketType(PROTOCOL, SENDER, 0x6C, "SetPlayerInventory");
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x6D, "SetPlayerTeam", "ScoreboardTeam", "SPacketTeams");
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x6E, "SetScore", "ScoreboardScore", "SPacketUpdateScore");
public static final PacketType UPDATE_SIMULATION_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x6F, "SetSimulationDistance");
public static final PacketType SET_SUBTITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x70, "SetSubtitleText");
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x71, "SetTime", "UpdateTime", "SPacketTimeUpdate");
public static final PacketType SET_TITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x72, "SetTitleText");
public static final PacketType SET_TITLES_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x73, "SetTitlesAnimation");
public static final PacketType ENTITY_SOUND = new PacketType(PROTOCOL, SENDER, 0x74, "SoundEntity", "EntitySound", "SPacketSoundEffect");
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x75, "Sound", "NamedSoundEffect");
public static final PacketType START_CONFIGURATION = new PacketType(PROTOCOL, SENDER, 0x76, "StartConfiguration");
public static final PacketType STOP_SOUND = new PacketType(PROTOCOL, SENDER, 0x77, "StopSound");
public static final PacketType STORE_COOKIE = new PacketType(PROTOCOL, SENDER, 0x78, "StoreCookie");
public static final PacketType SYSTEM_CHAT = new PacketType(PROTOCOL, SENDER, 0x79, "SystemChat");
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x7A, "TabList", "PlayerListHeaderFooter", "SPacketPlayerListHeaderFooter");
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x7B, "TagQuery", "NBTQuery");
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x7C, "TakeItemEntity", "Collect", "SPacketCollectItem");
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x7D, "TeleportEntity", "EntityTeleport", "SPacketEntityTeleport");
public static final PacketType TEST_INSTANCE_BLOCK_STATUS = new PacketType(PROTOCOL, SENDER, 0x7E, "ClientboundTestInstanceBlockStatus");
public static final PacketType TICKING_STATE = new PacketType(PROTOCOL, SENDER, 0x7F, "TickingState", "TickingStatePacket");
public static final PacketType TICKING_STEP_STATE = new PacketType(PROTOCOL, SENDER, 0x80, "TickingStep", "TickingStepPacket");
public static final PacketType TRANSFER = new PacketType(PROTOCOL, SENDER, 0x81, "Transfer");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x82, "UpdateAdvancements", "Advancements", "SPacketAdvancementInfo");
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x83, "UpdateAttributes", "SPacketEntityProperties");
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x84, "UpdateMobEffect", "EntityEffect", "SPacketEntityEffect");
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x85, "UpdateRecipes", "RecipeUpdate");
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x86, "UpdateTags", "Tags");
public static final PacketType PROJECTILE_POWER = new PacketType(PROTOCOL, SENDER, 0x87, "ProjectilePower");
public static final PacketType REPORT_DETAILS = new PacketType(PROTOCOL, SENDER, 0x88, "CustomReportDetails");
public static final PacketType SERVER_LINKS = new PacketType(PROTOCOL, SENDER, 0x89, "ServerLinks");
public static final PacketType TRACKED_WAYPOINT = new PacketType(PROTOCOL, SENDER, 0x8A, "TrackedWaypoint");
public static final PacketType CLEAR_DIALOG = new PacketType(PROTOCOL, SENDER, 0x8B, "ClearDialog");
public static final PacketType SHOW_DIALOG = new PacketType(PROTOCOL, SENDER, 0x8C, "ShowDialog");
public static final PacketType DEBUG_SAMPLE = new PacketType(PROTOCOL, SENDER, 0x1A, "DebugSample");
public static final PacketType DELETE_CHAT_MESSAGE = new PacketType(PROTOCOL, SENDER, 0x1B, "DeleteChat");
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x1C, "Disconnect", "KickDisconnect", "SPacketDisconnect");
public static final PacketType DISGUISED_CHAT = new PacketType(PROTOCOL, SENDER, 0x1D, "DisguisedChat");
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x1E, "EntityEvent", "EntityStatus", "SPacketEntityStatus");
public static final PacketType ENTITY_POSITION_SYNC = new PacketType(PROTOCOL, SENDER, 0x1F, "EntityPositionSync");
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x20, "Explode", "Explosion", "SPacketExplosion");
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x21, "ForgetLevelChunk", "UnloadChunk", "SPacketUnloadChunk");
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x22, "GameEvent", "GameStateChange", "SPacketChangeGameState");
public static final PacketType OPEN_WINDOW_HORSE = new PacketType(PROTOCOL, SENDER, 0x23, "HorseScreenOpen", "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, "LevelEvent", "WorldEvent", "SPacketEffect");
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x29, "LevelParticles", "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, "MapItemData", "Map", "SPacketMaps");
public static final PacketType OPEN_WINDOW_MERCHANT = new PacketType(PROTOCOL, SENDER, 0x2D, "MerchantOffers", "OpenWindowMerchant");
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x2E, "net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Pos", "Entity$RelEntityMove", "Entity$PacketPlayOutRelEntityMove");
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x2F, "net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$PosRot", "Entity$RelEntityMoveLook", "Entity$PacketPlayOutRelEntityMoveLook");
public static final PacketType MOVE_MINECART = new PacketType(PROTOCOL, SENDER, 0x30, "MoveMinecart");
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x31, "net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Rot", "Entity$EntityLook", "Entity$PacketPlayOutEntityLook");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x32, "MoveVehicle", "VehicleMove", "SPacketMoveVehicle");
public static final PacketType OPEN_BOOK = new PacketType(PROTOCOL, SENDER, 0x33, "OpenBook");
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x34, "OpenScreen", "OpenWindow", "SPacketOpenWindow");
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x35, "OpenSignEditor", "SPacketSignEditorOpen");
public static final PacketType PING = new PacketType(PROTOCOL, SENDER, 0x36, "Ping");
public static final PacketType PONG_RESPONSE = new PacketType(PROTOCOL, SENDER, 0x37, "PongResponse");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x38, "PlaceGhostRecipe", "AutoRecipe", "SPacketPlaceGhostRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x39, "PlayerAbilities", "Abilities", "SPacketPlayerAbilities");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x3A, "PlayerChat", "Chat", "SPacketChat");
public static final PacketType PLAYER_COMBAT_END = new PacketType(PROTOCOL, SENDER, 0x3B, "PlayerCombatEnd");
public static final PacketType PLAYER_COMBAT_ENTER = new PacketType(PROTOCOL, SENDER, 0x3C, "PlayerCombatEnter");
public static final PacketType PLAYER_COMBAT_KILL = new PacketType(PROTOCOL, SENDER, 0x3D, "PlayerCombatKill");
public static final PacketType PLAYER_INFO_REMOVE = new PacketType(PROTOCOL, SENDER, 0x3E, "PlayerInfoRemove");
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x3F, "PlayerInfoUpdate", "PlayerInfo");
public static final PacketType LOOK_AT = new PacketType(PROTOCOL, SENDER, 0x40, "PlayerLookAt", "LookAt", "SPacketPlayerPosLook");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x41, "PlayerPosition", "Position");
public static final PacketType PLAYER_ROTATION = new PacketType(PROTOCOL, SENDER, 0x42, "PlayerRotation");
public static final PacketType RECIPE_BOOK_ADD = new PacketType(PROTOCOL, SENDER, 0x43, "RecipeBookAdd");
public static final PacketType RECIPE_BOOK_REMOVE = new PacketType(PROTOCOL, SENDER, 0x44, "RecipeBookRemove");
public static final PacketType RECIPE_BOOK_SETTINGS = new PacketType(PROTOCOL, SENDER, 0x45, "RecipeBookSettings");
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x46, "RemoveEntities", "EntityDestroy", "SPacketDestroyEntities");
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x47, "RemoveMobEffect", "RemoveEntityEffect", "SPacketRemoveEntityEffect");
public static final PacketType RESET_SCORE = new PacketType(PROTOCOL, SENDER, 0x48, "ResetScore", "ResetScorePacket");
public static final PacketType REMOVE_RESOURCE_PACK = new PacketType(PROTOCOL, SENDER, 0x49, "ResourcePackPop", "ResourcePackPopPacket");
public static final PacketType ADD_RESOURCE_PACK = new PacketType(PROTOCOL, SENDER, 0x4A, "ResourcePackPush", "ResourcePackPushPacket");
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x4B, "Respawn", "SPacketRespawn");
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x4C, "RotateHead", "EntityHeadRotation", "SPacketEntityHeadLook");
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x4D, "SectionBlocksUpdate", "MultiBlockChange", "SPacketMultiBlockChange");
public static final PacketType SELECT_ADVANCEMENT_TAB = new PacketType(PROTOCOL, SENDER, 0x4E, "SelectAdvancementsTab", "SelectAdvancementTab", "SPacketSelectAdvancementsTab");
public static final PacketType SERVER_DATA = new PacketType(PROTOCOL, SENDER, 0x4F, "ServerData");
public static final PacketType SET_ACTION_BAR_TEXT = new PacketType(PROTOCOL, SENDER, 0x50, "SetActionBarText");
public static final PacketType SET_BORDER_CENTER = new PacketType(PROTOCOL, SENDER, 0x51, "SetBorderCenter");
public static final PacketType SET_BORDER_LERP_SIZE = new PacketType(PROTOCOL, SENDER, 0x52, "SetBorderLerpSize");
public static final PacketType SET_BORDER_SIZE = new PacketType(PROTOCOL, SENDER, 0x53, "SetBorderSize");
public static final PacketType SET_BORDER_WARNING_DELAY = new PacketType(PROTOCOL, SENDER, 0x54, "SetBorderWarningDelay");
public static final PacketType SET_BORDER_WARNING_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x55, "SetBorderWarningDistance");
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x56, "SetCamera", "Camera", "SPacketCamera");
public static final PacketType VIEW_CENTRE = new PacketType(PROTOCOL, SENDER, 0x57, "SetChunkCacheCenter", "ViewCentre");
public static final PacketType VIEW_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x58, "SetChunkCacheRadius", "ViewDistance");
public static final PacketType SET_CURSOR_ITEM = new PacketType(PROTOCOL, SENDER, 0x59, "SetCursorItem");
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x5A, "SetDefaultSpawnPosition", "SpawnPosition", "SPacketSpawnPosition");
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x5B, "SetDisplayObjective", "ScoreboardDisplayObjective", "SPacketDisplayObjective");
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x5C, "SetEntityData", "EntityMetadata", "SPacketEntityMetadata");
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x5D, "SetEntityLink", "AttachEntity", "SPacketEntityAttach");
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x5E, "SetEntityMotion", "EntityVelocity", "SPacketEntityVelocity");
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x5F, "SetEquipment", "EntityEquipment", "SPacketEntityEquipment");
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x60, "SetExperience", "Experience", "SPacketSetExperience");
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x61, "SetHealth", "UpdateHealth", "SPacketUpdateHealth");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x62, "SetHeldSlot", "SetCarriedItem", "HeldItemSlot", "SPacketHeldItemChange");
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x63, "SetObjective", "ScoreboardObjective", "SPacketScoreboardObjective");
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x64, "SetPassengers", "Mount", "SPacketSetPassengers");
public static final PacketType SET_PLAYER_INVENTORY = new PacketType(PROTOCOL, SENDER, 0x65, "SetPlayerInventory");
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x66, "SetPlayerTeam", "ScoreboardTeam", "SPacketTeams");
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x67, "SetScore", "ScoreboardScore", "SPacketUpdateScore");
public static final PacketType UPDATE_SIMULATION_DISTANCE = new PacketType(PROTOCOL, SENDER, 0x68, "SetSimulationDistance");
public static final PacketType SET_SUBTITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x69, "SetSubtitleText");
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x6A, "SetTime", "UpdateTime", "SPacketTimeUpdate");
public static final PacketType SET_TITLE_TEXT = new PacketType(PROTOCOL, SENDER, 0x6B, "SetTitleText");
public static final PacketType SET_TITLES_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x6C, "SetTitlesAnimation");
public static final PacketType ENTITY_SOUND = new PacketType(PROTOCOL, SENDER, 0x6D, "SoundEntity", "EntitySound", "SPacketSoundEffect");
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x6E, "Sound", "NamedSoundEffect");
public static final PacketType START_CONFIGURATION = new PacketType(PROTOCOL, SENDER, 0x6F, "StartConfiguration");
public static final PacketType STOP_SOUND = new PacketType(PROTOCOL, SENDER, 0x70, "StopSound");
public static final PacketType STORE_COOKIE = new PacketType(PROTOCOL, SENDER, 0x71, "StoreCookie");
public static final PacketType SYSTEM_CHAT = new PacketType(PROTOCOL, SENDER, 0x72, "SystemChat");
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x73, "TabList", "PlayerListHeaderFooter", "SPacketPlayerListHeaderFooter");
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x74, "TagQuery", "NBTQuery");
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x75, "TakeItemEntity", "Collect", "SPacketCollectItem");
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x76, "TeleportEntity", "EntityTeleport", "SPacketEntityTeleport");
public static final PacketType TEST_INSTANCE_BLOCK_STATUS = new PacketType(PROTOCOL, SENDER, 0x77, "ClientboundTestInstanceBlockStatus");
public static final PacketType TICKING_STATE = new PacketType(PROTOCOL, SENDER, 0x78, "TickingState", "TickingStatePacket");
public static final PacketType TICKING_STEP_STATE = new PacketType(PROTOCOL, SENDER, 0x79, "TickingStep", "TickingStepPacket");
public static final PacketType TRANSFER = new PacketType(PROTOCOL, SENDER, 0x7A, "Transfer");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x7B, "UpdateAdvancements", "Advancements", "SPacketAdvancementInfo");
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x7C, "UpdateAttributes", "SPacketEntityProperties");
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x7D, "UpdateMobEffect", "EntityEffect", "SPacketEntityEffect");
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x7E, "UpdateRecipes", "RecipeUpdate");
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x7F, "UpdateTags", "Tags");
public static final PacketType PROJECTILE_POWER = new PacketType(PROTOCOL, SENDER, 0x80, "ProjectilePower");
public static final PacketType REPORT_DETAILS = new PacketType(PROTOCOL, SENDER, 0x81, "CustomReportDetails");
public static final PacketType SERVER_LINKS = new PacketType(PROTOCOL, SENDER, 0x82, "ServerLinks");
public static final PacketType TRACKED_WAYPOINT = new PacketType(PROTOCOL, SENDER, 0x83, "TrackedWaypoint");
public static final PacketType CLEAR_DIALOG = new PacketType(PROTOCOL, SENDER, 0x84, "ClearDialog");
public static final PacketType SHOW_DIALOG = new PacketType(PROTOCOL, SENDER, 0x85, "ShowDialog");
/**
* @deprecated Removed in 1.9
@@ -423,6 +416,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
*/
@Deprecated
public static final PacketType SPAWN_ENTITY_EXPERIENCE_ORB = new PacketType(PROTOCOL, SENDER, 232, "AddExperienceOrb", "SpawnEntityExperienceOrb", "SPacketSpawnExperienceOrb");
private static final Server INSTANCE = new Server();
@@ -445,74 +439,71 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
private static final Sender SENDER = Sender.CLIENT;
public static final PacketType TELEPORT_ACCEPT = new PacketType(PROTOCOL, SENDER, 0x00, "AcceptTeleportation", "TeleportAccept", "CPacketConfirmTeleport");
public static final PacketType ATTACK = new PacketType(PROTOCOL, SENDER, 0x01, "Attack");
public static final PacketType TILE_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x02, "BlockEntityTagQuery", "TileNBTQuery");
public static final PacketType SELECT_BUNDLE_ITEM = new PacketType(PROTOCOL, SENDER, 0x03, "SelectBundleItem");
public static final PacketType DIFFICULTY_CHANGE = new PacketType(PROTOCOL, SENDER, 0x04, "ChangeDifficulty", "DifficultyChange");
public static final PacketType CHANGE_GAME_MODE = new PacketType(PROTOCOL, SENDER, 0x05, "ChangeGameMode");
public static final PacketType CHAT_ACK = new PacketType(PROTOCOL, SENDER, 0x06, "ChatAck");
public static final PacketType CHAT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x07, "ChatCommand");
public static final PacketType CHAT_COMMAND_SIGNED = new PacketType(PROTOCOL, SENDER, 0x08, "ChatCommandSigned");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x09, "Chat", "CPacketChatMessage");
public static final PacketType CHAT_SESSION_UPDATE = new PacketType(PROTOCOL, SENDER, 0x0A, "ChatSessionUpdate");
public static final PacketType CHUNK_BATCH_RECEIVED = new PacketType(PROTOCOL, SENDER, 0x0B, "ChunkBatchReceived");
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x0C, "ClientCommand", "CPacketClientStatus");
public static final PacketType CLIENT_TICK_END = new PacketType(PROTOCOL, SENDER, 0x0D, "ClientTickEnd");
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x0E, "ClientInformation", "Settings", "CPacketClientSettings");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x0F, "CommandSuggestion", "TabComplete", "CPacketTabComplete");
public static final PacketType CONFIGURATION_ACK = new PacketType(PROTOCOL, SENDER, 0x10, "ConfigurationAcknowledged");
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x11, "ContainerButtonClick", "EnchantItem", "CPacketEnchantItem");
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x12, "ContainerClick", "WindowClick", "CPacketClickWindow");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x13, "ContainerClose", "CloseWindow", "CPacketCloseWindow");
public static final PacketType CONTAINER_SLOT_STATE_CHANGED = new PacketType(PROTOCOL, SENDER, 0x14, "ContainerSlotStateChanged", "ContainerSlotStateChangedPacket");
public static final PacketType COOKIE_RESPONSE = new PacketType(PROTOCOL, SENDER, 0x15, "CookieResponse");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x16, "CustomPayload", "CPacketCustomPayload");
public static final PacketType DEBUG_SUBSCRIPTION_REQUEST = new PacketType(PROTOCOL, SENDER, 0x17, "DebugSubscriptionRequest");
public static final PacketType B_EDIT = new PacketType(PROTOCOL, SENDER, 0x18, "EditBook", "BEdit");
public static final PacketType ENTITY_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x19, "EntityTagQuery", "EntityNBTQuery");
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x1A, "Interact", "UseEntity", "CPacketUseEntity");
public static final PacketType JIGSAW_GENERATE = new PacketType(PROTOCOL, SENDER, 0x1B, "JigsawGenerate");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x1C, "KeepAlive", "CPacketKeepAlive");
public static final PacketType DIFFICULTY_LOCK = new PacketType(PROTOCOL, SENDER, 0x1D, "LockDifficulty", "DifficultyLock");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x1E, "net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Pos", "Flying$Position", "Flying$PacketPlayInPosition", "CPacketPlayer$Position");
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x1F, "net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$PosRot", "Flying$PositionLook", "Flying$PacketPlayInPositionLook", "CPacketPlayer$PositionRotation");
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x20, "net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Rot", "Flying$Look", "Flying$PacketPlayInLook", "CPacketPlayer$Rotation");
public static final PacketType GROUND = new PacketType(PROTOCOL, SENDER, 0x21, "net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$StatusOnly", "Flying$d");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x22, "MoveVehicle", "VehicleMove", "CPacketVehicleMove");
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x23, "PaddleBoat", "BoatMove", "CPacketSteerBoat");
public static final PacketType PICK_ITEM_FROM_BLOCK = new PacketType(PROTOCOL, SENDER, 0x24, "PickItemFromBlock");
public static final PacketType PICK_ITEM = new PacketType(PROTOCOL, SENDER, 0x25, "PickItemFromEntity", "PickItem");
public static final PacketType PING_REQUEST = new PacketType(PROTOCOL, SENDER, 0x26, "PingRequest");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x27, "PlaceRecipe", "AutoRecipe", "CPacketPlaceRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x28, "PlayerAbilities", "Abilities", "CPacketPlayerAbilities");
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x29, "PlayerAction", "BlockDig", "CPacketPlayerDigging");
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x2A, "PlayerCommand", "EntityAction", "CPacketEntityAction");
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x2B, "PlayerInput", "SteerVehicle", "CPacketInput");
public static final PacketType PLAYER_LOADED = new PacketType(PROTOCOL, SENDER, 0x2C, "PlayerLoaded");
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x2D, "Pong");
public static final PacketType RECIPE_SETTINGS = new PacketType(PROTOCOL, SENDER, 0x2E, "RecipeBookChangeSettings", "RecipeSettings");
public static final PacketType RECIPE_DISPLAYED = new PacketType(PROTOCOL, SENDER, 0x2F, "RecipeBookSeenRecipe", "RecipeDisplayed", "CPacketRecipeInfo");
public static final PacketType ITEM_NAME = new PacketType(PROTOCOL, SENDER, 0x30, "RenameItem", "ItemName");
public static final PacketType RESOURCE_PACK_STATUS = new PacketType(PROTOCOL, SENDER, 0x31, "ResourcePack", "ResourcePackStatus", "CPacketResourcePackStatus");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x32, "SeenAdvancements", "Advancements", "CPacketSeenAdvancements");
public static final PacketType TR_SEL = new PacketType(PROTOCOL, SENDER, 0x33, "SelectTrade", "TrSel");
public static final PacketType BEACON = new PacketType(PROTOCOL, SENDER, 0x34, "SetBeacon", "Beacon");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x35, "SetCarriedItem", "HeldItemSlot", "CPacketHeldItemChange");
public static final PacketType SET_COMMAND_BLOCK = new PacketType(PROTOCOL, SENDER, 0x36, "SetCommandBlock");
public static final PacketType SET_COMMAND_MINECART = new PacketType(PROTOCOL, SENDER, 0x37, "SetCommandMinecart");
public static final PacketType SET_CREATIVE_SLOT = new PacketType(PROTOCOL, SENDER, 0x38, "SetCreativeModeSlot", "SetCreativeSlot", "CPacketCreativeInventoryAction");
public static final PacketType SET_GAME_RULE = new PacketType(PROTOCOL, SENDER, 0x39, "SetGameRule");
public static final PacketType SET_JIGSAW = new PacketType(PROTOCOL, SENDER, 0x3A, "SetJigsawBlock", "SetJigsaw");
public static final PacketType STRUCT = new PacketType(PROTOCOL, SENDER, 0x3B, "SetStructureBlock", "Struct");
public static final PacketType SET_TEST_BLOCK = new PacketType(PROTOCOL, SENDER, 0x3C, "SetTestBlock");
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x3D, "SignUpdate", "UpdateSign", "CPacketUpdateSign");
public static final PacketType SPECTATE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x3E, "SpectatorAction", "SpectateEntity");
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x3F, "Swing", "ArmAnimation", "CPacketAnimation");
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x40, "TeleportToEntity", "Spectate", "CPacketSpectate");
public static final PacketType TEST_INSTANCE_BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x41, "TestInstanceBlockAction");
public static final PacketType USE_ITEM_ON = new PacketType(PROTOCOL, SENDER, 0x42, "UseItemOn", "BlockPlace", "CPacketPlayerTryUseItemOnBlock");
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x43, "UseItem", "CPacketPlayerTryUseItem");
public static final PacketType CUSTOM_CLICK_ACTION = new PacketType(PROTOCOL, SENDER, 0x44, "CustomClickAction");
public static final PacketType TILE_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x01, "BlockEntityTagQuery", "TileNBTQuery");
public static final PacketType SELECT_BUNDLE_ITEM = new PacketType(PROTOCOL, SENDER, 0x02, "SelectBundleItem");
public static final PacketType DIFFICULTY_CHANGE = new PacketType(PROTOCOL, SENDER, 0x03, "ChangeDifficulty", "DifficultyChange");
public static final PacketType CHANGE_GAME_MODE = new PacketType(PROTOCOL, SENDER, 0x04, "ChangeGameMode");
public static final PacketType CHAT_ACK = new PacketType(PROTOCOL, SENDER, 0x05, "ChatAck");
public static final PacketType CHAT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x06, "ChatCommand");
public static final PacketType CHAT_COMMAND_SIGNED = new PacketType(PROTOCOL, SENDER, 0x07, "ChatCommandSigned");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x08, "Chat", "CPacketChatMessage");
public static final PacketType CHAT_SESSION_UPDATE = new PacketType(PROTOCOL, SENDER, 0x09, "ChatSessionUpdate");
public static final PacketType CHUNK_BATCH_RECEIVED = new PacketType(PROTOCOL, SENDER, 0x0A, "ChunkBatchReceived");
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x0B, "ClientCommand", "CPacketClientStatus");
public static final PacketType CLIENT_TICK_END = new PacketType(PROTOCOL, SENDER, 0x0C, "ClientTickEnd");
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x0D, "ClientInformation", "Settings", "CPacketClientSettings");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x0E, "CommandSuggestion", "TabComplete", "CPacketTabComplete");
public static final PacketType CONFIGURATION_ACK = new PacketType(PROTOCOL, SENDER, 0x0F, "ConfigurationAcknowledged");
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x10, "ContainerButtonClick", "EnchantItem", "CPacketEnchantItem");
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x11, "ContainerClick", "WindowClick", "CPacketClickWindow");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x12, "ContainerClose", "CloseWindow", "CPacketCloseWindow");
public static final PacketType CONTAINER_SLOT_STATE_CHANGED = new PacketType(PROTOCOL, SENDER, 0x13, "ContainerSlotStateChanged", "ContainerSlotStateChangedPacket");
public static final PacketType COOKIE_RESPONSE = new PacketType(PROTOCOL, SENDER, 0x14, "CookieResponse");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x15, "CustomPayload", "CPacketCustomPayload");
public static final PacketType DEBUG_SAMPLE_SUBSCRIPTION = new PacketType(PROTOCOL, SENDER, 0x16, "DebugSampleSubscription");
public static final PacketType B_EDIT = new PacketType(PROTOCOL, SENDER, 0x17, "EditBook", "BEdit");
public static final PacketType ENTITY_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x18, "EntityTagQuery", "EntityNBTQuery");
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x19, "Interact", "UseEntity", "CPacketUseEntity");
public static final PacketType JIGSAW_GENERATE = new PacketType(PROTOCOL, SENDER, 0x1A, "JigsawGenerate");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x1B, "KeepAlive", "CPacketKeepAlive");
public static final PacketType DIFFICULTY_LOCK = new PacketType(PROTOCOL, SENDER, 0x1C, "LockDifficulty", "DifficultyLock");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x1D, "net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Pos", "Flying$Position", "Flying$PacketPlayInPosition", "CPacketPlayer$Position");
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x1E, "net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$PosRot", "Flying$PositionLook", "Flying$PacketPlayInPositionLook", "CPacketPlayer$PositionRotation");
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x1F, "net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Rot", "Flying$Look", "Flying$PacketPlayInLook", "CPacketPlayer$Rotation");
public static final PacketType GROUND = new PacketType(PROTOCOL, SENDER, 0x20, "net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$StatusOnly", "Flying$d");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x21, "MoveVehicle", "VehicleMove", "CPacketVehicleMove");
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x22, "PaddleBoat", "BoatMove", "CPacketSteerBoat");
public static final PacketType PICK_ITEM_FROM_BLOCK = new PacketType(PROTOCOL, SENDER, 0x23, "PickItemFromBlock");
public static final PacketType PICK_ITEM = new PacketType(PROTOCOL, SENDER, 0x24, "PickItemFromEntity", "PickItem");
public static final PacketType PING_REQUEST = new PacketType(PROTOCOL, SENDER, 0x25, "PingRequest");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x26, "PlaceRecipe", "AutoRecipe", "CPacketPlaceRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x27, "PlayerAbilities", "Abilities", "CPacketPlayerAbilities");
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x28, "PlayerAction", "BlockDig", "CPacketPlayerDigging");
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x29, "PlayerCommand", "EntityAction", "CPacketEntityAction");
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x2A, "PlayerInput", "SteerVehicle", "CPacketInput");
public static final PacketType PLAYER_LOADED = new PacketType(PROTOCOL, SENDER, 0x2B, "PlayerLoaded");
public static final PacketType PONG = new PacketType(PROTOCOL, SENDER, 0x2C, "Pong");
public static final PacketType RECIPE_SETTINGS = new PacketType(PROTOCOL, SENDER, 0x2D, "RecipeBookChangeSettings", "RecipeSettings");
public static final PacketType RECIPE_DISPLAYED = new PacketType(PROTOCOL, SENDER, 0x2E, "RecipeBookSeenRecipe", "RecipeDisplayed", "CPacketRecipeInfo");
public static final PacketType ITEM_NAME = new PacketType(PROTOCOL, SENDER, 0x2F, "RenameItem", "ItemName");
public static final PacketType RESOURCE_PACK_STATUS = new PacketType(PROTOCOL, SENDER, 0x30, "ResourcePack", "ResourcePackStatus", "CPacketResourcePackStatus");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x31, "SeenAdvancements", "Advancements", "CPacketSeenAdvancements");
public static final PacketType TR_SEL = new PacketType(PROTOCOL, SENDER, 0x32, "SelectTrade", "TrSel");
public static final PacketType BEACON = new PacketType(PROTOCOL, SENDER, 0x33, "SetBeacon", "Beacon");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x34, "SetCarriedItem", "HeldItemSlot", "CPacketHeldItemChange");
public static final PacketType SET_COMMAND_BLOCK = new PacketType(PROTOCOL, SENDER, 0x35, "SetCommandBlock");
public static final PacketType SET_COMMAND_MINECART = new PacketType(PROTOCOL, SENDER, 0x36, "SetCommandMinecart");
public static final PacketType SET_CREATIVE_SLOT = new PacketType(PROTOCOL, SENDER, 0x37, "SetCreativeModeSlot", "SetCreativeSlot", "CPacketCreativeInventoryAction");
public static final PacketType SET_JIGSAW = new PacketType(PROTOCOL, SENDER, 0x38, "SetJigsawBlock", "SetJigsaw");
public static final PacketType STRUCT = new PacketType(PROTOCOL, SENDER, 0x39, "SetStructureBlock", "Struct");
public static final PacketType SET_TEST_BLOCK = new PacketType(PROTOCOL, SENDER, 0x3A, "SetTestBlock");
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x3B, "SignUpdate", "UpdateSign", "CPacketUpdateSign");
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x3C, "Swing", "ArmAnimation", "CPacketAnimation");
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x3D, "TeleportToEntity", "Spectate", "CPacketSpectate");
public static final PacketType TEST_INSTANCE_BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x3E, "TestInstanceBlockAction");
public static final PacketType USE_ITEM_ON = new PacketType(PROTOCOL, SENDER, 0x3F, "UseItemOn", "BlockPlace", "CPacketPlayerTryUseItemOnBlock");
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x40, "UseItem", "CPacketPlayerTryUseItem");
public static final PacketType CUSTOM_CLICK_ACTION = new PacketType(PROTOCOL, SENDER, 0x41, "CustomClickAction");
/**
* @deprecated Removed in 1.17
@@ -532,12 +523,6 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
@Deprecated
public static final PacketType CHAT_PREVIEW = new PacketType(PROTOCOL, SENDER, 253, "ChatPreview");
/**
* deprecated Removed in 1.21.9
*/
@Deprecated
public static final PacketType DEBUG_SAMPLE_SUBSCRIPTION = new PacketType(PROTOCOL, SENDER, 0x16, "DebugSampleSubscription");
/**
* @deprecated Renamed to USE_ITEM_ON
*/
@@ -728,7 +713,6 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType SERVER_LINKS = new PacketType(PROTOCOL, SENDER, 0x10, "ServerLinks");
public static final PacketType CLEAR_DIALOG = new PacketType(PROTOCOL, SENDER, 0x11, "ClearDialog");
public static final PacketType SHOW_DIALOG = new PacketType(PROTOCOL, SENDER, 0x12, "ShowDialog");
public static final PacketType CODE_OF_CONDUCT = new PacketType(PROTOCOL, SENDER, 0x13, "CodeOfConduct");
/**
* @deprecated Removed in 1.20.4: replaced with new packets for removing and sending resource packs
@@ -761,7 +745,6 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType RESOURCE_PACK_ACK = new PacketType(PROTOCOL, SENDER, 0x06, "ResourcePack");
public static final PacketType SELECT_KNOWN_PACKS = new PacketType(PROTOCOL, SENDER, 0x07, "ServerboundSelectKnownPacks");
public static final PacketType CUSTOM_CLICK_ACTION = new PacketType(PROTOCOL, SENDER, 0x08, "CustomClickAction");
public static final PacketType ACCEPT_CODE_OF_CONDUCT = new PacketType(PROTOCOL, SENDER, 0x09, "AcceptCodeOfConduct");
private static final Client INSTANCE = new Client();
@@ -0,0 +1,57 @@
package com.comphenix.protocol;
import java.lang.reflect.Field;
import java.util.logging.Level;
import com.comphenix.protocol.injector.PacketFilterManager;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import io.netty.channel.Channel;
import io.papermc.paper.connection.PlayerConnection;
import io.papermc.paper.connection.ReadablePlayerCookieConnectionImpl;
import io.papermc.paper.event.connection.PlayerConnectionValidateLoginEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
class PaperLoginListener implements Listener {
private final PacketFilterManager manager;
PaperLoginListener(PacketFilterManager manager) {
this.manager = manager;
}
private static FieldAccessor networkManagerField;
private static FieldAccessor channelField;
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerConnectionValidateLogin(PlayerConnectionValidateLoginEvent event) {
try {
PlayerConnection connection = event.getConnection();
ProtocolLogger.log("onPlayerConnectionValidateLogin: " + connection.getAddress());
if (networkManagerField == null) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(ReadablePlayerCookieConnectionImpl.class, true);
Field field = fuzzy.getFieldByType("networkManager", MinecraftReflection.getNetworkManagerClass());
networkManagerField = Accessors.getFieldAccessor(field);
}
Object networkManager = networkManagerField.get(connection);
if (channelField == null) {
FuzzyReflection fuzzy = FuzzyReflection.fromObject(networkManager, true);
Field field = fuzzy.getFieldByType("channel", Channel.class);
channelField = Accessors.getFieldAccessor(field);
}
Channel channel = (Channel) channelField.get(networkManager);
manager.injectChannel(channel);
ProtocolLogger.log("Successfully injected channel " + channel);
} catch (Throwable ex) {
ProtocolLogger.log(Level.SEVERE, "Failed to inject channel", ex);
}
}
}
@@ -15,26 +15,6 @@
*/
package com.comphenix.protocol;
import com.comphenix.protocol.async.AsyncFilterManager;
import com.comphenix.protocol.error.BasicErrorReporter;
import com.comphenix.protocol.error.DelegatedErrorReporter;
import com.comphenix.protocol.error.DetailedErrorReporter;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.injector.InternalManager;
import com.comphenix.protocol.injector.PacketFilterManager;
import com.comphenix.protocol.metrics.Statistics;
import com.comphenix.protocol.scheduler.DefaultScheduler;
import com.comphenix.protocol.scheduler.FoliaScheduler;
import com.comphenix.protocol.scheduler.ProtocolScheduler;
import com.comphenix.protocol.scheduler.Task;
import com.comphenix.protocol.updater.Updater;
import com.comphenix.protocol.updater.Updater.UpdateType;
import com.comphenix.protocol.utility.*;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -47,9 +27,29 @@ import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import com.comphenix.protocol.async.AsyncFilterManager;
import com.comphenix.protocol.error.BasicErrorReporter;
import com.comphenix.protocol.error.DelegatedErrorReporter;
import com.comphenix.protocol.error.DetailedErrorReporter;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.injector.PacketFilterManager;
import com.comphenix.protocol.metrics.Statistics;
import com.comphenix.protocol.scheduler.DefaultScheduler;
import com.comphenix.protocol.scheduler.FoliaScheduler;
import com.comphenix.protocol.scheduler.ProtocolScheduler;
import com.comphenix.protocol.scheduler.Task;
import com.comphenix.protocol.updater.Updater;
import com.comphenix.protocol.updater.Updater.UpdateType;
import com.comphenix.protocol.utility.ByteBuddyFactory;
import com.comphenix.protocol.utility.ChatExtensions;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.utility.Util;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import org.bukkit.Server;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
@@ -58,7 +58,7 @@ import org.bukkit.plugin.java.JavaPlugin;
*
* @author Kristian
*/
public class ProtocolLib extends JavaPlugin {
public abstract class ProtocolLib extends JavaPlugin {
// Every possible error or warning report type
public static final ReportType REPORT_CANNOT_DELETE_CONFIG = new ReportType(
"Cannot delete old ProtocolLib configuration.");
@@ -91,8 +91,8 @@ public class ProtocolLib extends JavaPlugin {
// these fields are only existing once, we can make them static
private static Logger logger;
private static ProtocolConfig config;
private static InternalManager protocolManager;
private static ErrorReporter reporter = new BasicErrorReporter();
protected static PacketFilterManager protocolManager;
protected static ErrorReporter reporter = new BasicErrorReporter();
private Statistics statistics;
@@ -210,7 +210,7 @@ public class ProtocolLib extends JavaPlugin {
this.commandPacket = new CommandPacket(reporter, this, logger, this.commandFilter, protocolManager);
break;
case LOGGING:
this.packetLogging = new PacketLogging(this, protocolManager);
this.packetLogging = new PacketLogging(reporter, this, protocolManager);
break;
}
} catch (OutOfMemoryError e) {
@@ -336,7 +336,11 @@ public class ProtocolLib extends JavaPlugin {
this.registerCommand(PacketLogging.NAME, this.packetLogging);
// Player login and logout events
protocolManager.registerEvents(manager, this);
protocolManager.inject();
manager.registerEvents(new BukkitListener(protocolManager), this);
registerLoginListener();
// Worker that ensures that async packets are eventually sent
// It also performs the update check.
@@ -364,6 +368,8 @@ public class ProtocolLib extends JavaPlugin {
}
}
abstract void registerLoginListener();
// Used to check Minecraft version
private MinecraftVersion verifyMinecraftVersion() {
MinecraftVersion minimum = new MinecraftVersion(ProtocolLibrary.MINIMUM_MINECRAFT_VERSION);
@@ -437,26 +443,7 @@ public class ProtocolLib extends JavaPlugin {
}
}
private void registerCommand(String name, CommandExecutor executor) {
try {
// Ignore these - they must have printed an error already
if (executor == null) {
return;
}
PluginCommand command = this.getCommand(name);
// Try to load the command
if (command != null) {
command.setExecutor(executor);
} else {
throw new RuntimeException("plugin.yml might be corrupt.");
}
} catch (RuntimeException e) {
reporter.reportWarning(this,
Report.newBuilder(REPORT_CANNOT_REGISTER_COMMAND).messageParam(name, e.getMessage()).error(e));
}
}
abstract void registerCommand(String name, CommandBase command);
/**
* Disable the current plugin.
@@ -0,0 +1,19 @@
package com.comphenix.protocol;
public final class ProtocolLibPaper extends ProtocolLib {
@Override
void registerLoginListener() {
try {
Class.forName("io.papermc.paper.event.connection.PlayerConnectionValidateLoginEvent");
getServer().getPluginManager().registerEvents(new com.comphenix.protocol.PaperLoginListener(protocolManager), this);
} catch (ClassNotFoundException ignored) {
getServer().getPluginManager().registerEvents(new LegacyLoginListener(protocolManager), this);
}
}
@Override
public void registerCommand(String name, CommandBase command) {
registerCommand(name, (stack, args) -> command.onCommand(stack.getSender(), args));
}
}
@@ -0,0 +1,30 @@
package com.comphenix.protocol;
import com.comphenix.protocol.error.Report;
import org.bukkit.command.PluginCommand;
public final class ProtocolLibSpigot extends ProtocolLib {
@Override
void registerLoginListener() {
getServer().getPluginManager().registerEvents(new LegacyLoginListener(protocolManager), this);
}
@Override
void registerCommand(String name, CommandBase command) {
try {
PluginCommand pluginCmd = this.getCommand(name);
// Try to load the command
if (pluginCmd != null) {
pluginCmd.setExecutor((sender, _c, _l, args) ->
command.onCommand(sender, args));
} else {
throw new RuntimeException("plugin.yml might be corrupt.");
}
} catch (RuntimeException e) {
reporter.reportWarning(this,
Report.newBuilder(REPORT_CANNOT_REGISTER_COMMAND).messageParam(name, e.getMessage()).error(e));
}
}
}
@@ -18,7 +18,7 @@ import com.comphenix.protocol.error.BasicErrorReporter;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.scheduler.ProtocolScheduler;
import org.apache.commons.lang.Validate;
import com.comphenix.protocol.utility.Validate;
import org.bukkit.plugin.Plugin;
/**
@@ -35,12 +35,12 @@ public class ProtocolLibrary {
/**
* The maximum version ProtocolLib has been tested with.
*/
public static final String MAXIMUM_MINECRAFT_VERSION = "26.2";
public static final String MAXIMUM_MINECRAFT_VERSION = "1.21.8";
/**
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (26.2) was released.
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.21.8) was released.
*/
public static final String MINECRAFT_LAST_RELEASE_DATE = "2026-06-16";
public static final String MINECRAFT_LAST_RELEASE_DATE = "2025-07-17";
private static Plugin plugin;
private static ProtocolConfig config;
@@ -48,8 +48,8 @@ public class ProtocolLibrary {
private static ProtocolScheduler scheduler;
private static ErrorReporter reporter = new BasicErrorReporter();
private static boolean updatesDisabled;
private static boolean initialized;
private static boolean updatesDisabled = false;
private static boolean initialized = false;
protected static void init(Plugin plugin, ProtocolConfig config, ProtocolManager manager,
ProtocolScheduler scheduler, ErrorReporter reporter) {
@@ -27,7 +27,6 @@ import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.injector.collection.InboundPacketListenerSet;
import com.comphenix.protocol.injector.collection.OutboundPacketListenerSet;
import com.comphenix.protocol.injector.netty.Injector;
import com.comphenix.protocol.scheduler.ProtocolScheduler;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
@@ -275,7 +274,7 @@ public class AsyncFilterManager implements AsynchronousManager {
* @return TRUE if we are, FALSE otherwise.
*/
private boolean onMainThread() {
return Thread.currentThread() == mainThread;
return Thread.currentThread().getId() == mainThread.getId();
}
@Override
@@ -348,8 +347,8 @@ public class AsyncFilterManager implements AsynchronousManager {
* Construct a asynchronous marker with all the default values.
* @return Asynchronous marker.
*/
public AsyncMarker createAsyncMarker(Injector injector) {
return createAsyncMarker(injector, AsyncMarker.DEFAULT_TIMEOUT_DELTA);
public AsyncMarker createAsyncMarker() {
return createAsyncMarker(AsyncMarker.DEFAULT_TIMEOUT_DELTA);
}
/**
@@ -357,13 +356,13 @@ public class AsyncFilterManager implements AsynchronousManager {
* @param timeoutDelta - how long (in ms) until the packet expire.
* @return An async marker.
*/
public AsyncMarker createAsyncMarker(Injector injector, long timeoutDelta) {
return createAsyncMarker(injector, timeoutDelta, currentSendingIndex.incrementAndGet());
public AsyncMarker createAsyncMarker(long timeoutDelta) {
return createAsyncMarker(timeoutDelta, currentSendingIndex.incrementAndGet());
}
// Helper method
private AsyncMarker createAsyncMarker(Injector injector, long timeoutDelta, long sendingIndex) {
return new AsyncMarker(injector, sendingIndex, System.currentTimeMillis(), timeoutDelta);
private AsyncMarker createAsyncMarker(long timeoutDelta, long sendingIndex) {
return new AsyncMarker(manager, sendingIndex, System.currentTimeMillis(), timeoutDelta);
}
@Override
@@ -538,7 +538,7 @@ public class AsyncListenerHandler {
*/
private void listenerLoop(int workerID) {
// Danger, danger!
if (Thread.currentThread() == mainThread)
if (Thread.currentThread().getId() == mainThread.getId())
throw new IllegalStateException("Do not call this method from the main thread.");
if (cancelled)
throw new IllegalStateException("Listener has been cancelled. Create a new listener instead.");
@@ -23,18 +23,14 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import com.comphenix.protocol.PacketStream;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.netty.Injector;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.utility.MinecraftReflection;
@@ -68,7 +64,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
/**
* The packet stream responsible for transmitting the packet when it's done processing.
*/
private final Injector injector;
private transient PacketStream packetStream;
/**
* Current list of async packet listeners.
@@ -90,7 +86,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
private volatile boolean processed;
// Whether or not the packet has been sent
private final AtomicBoolean transmitted = new AtomicBoolean(false);
private volatile boolean transmitted;
// Whether or not the asynchronous processing itself should be cancelled
private volatile boolean asyncCancelled;
@@ -113,8 +109,11 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
* Create a container for asyncronous packets.
* @param initialTime - the current time in milliseconds since 01.01.1970 00:00.
*/
AsyncMarker(Injector injector, long sendingIndex, long initialTime, long timeoutDelta) {
this.injector = Objects.requireNonNull(injector, "injector is nul");
AsyncMarker(PacketStream packetStream, long sendingIndex, long initialTime, long timeoutDelta) {
if (packetStream == null)
throw new IllegalArgumentException("packetStream cannot be NULL");
this.packetStream = packetStream;
// Timeout
this.initialTime = initialTime;
@@ -181,18 +180,16 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
* Retrieve the packet stream responsible for transmitting this packet.
* @return The packet stream.
*/
@Deprecated
public PacketStream getPacketStream() {
return ProtocolLibrary.getProtocolManager();
return packetStream;
}
/**
* Sets the output packet stream responsible for transmitting this packet.
* @param packetStream - new output packet stream.
*/
@Deprecated
public void setPacketStream(PacketStream packetStream) {
// NOOP
this.packetStream = packetStream;
}
/**
@@ -288,7 +285,7 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
* @return TRUE if it has been sent before, FALSE otherwise.
*/
public boolean isTransmitted() {
return transmitted.get();
return transmitted;
}
/**
@@ -386,18 +383,13 @@ public class AsyncMarker implements Serializable, Comparable<AsyncMarker> {
* @throws IOException If the packet couldn't be sent.
*/
void sendPacket(PacketEvent event) throws IOException {
if (!this.transmitted.compareAndSet(false, true)) {
return;
}
Object handle = event.getPacket().getHandle();
if (event.isServerPacket()) {
NetworkMarker marker = NetworkMarker.getNetworkMarker(event);
this.injector.sendClientboundPacket(handle, marker, false);
packetStream.sendServerPacket(event.getPlayer(), event.getPacket(), NetworkMarker.getNetworkMarker(event), false);
} else {
this.injector.readServerboundPacket(handle);
packetStream.receiveClientPacket(event.getPlayer(), event.getPacket(), NetworkMarker.getNetworkMarker(event),
false);
}
transmitted = true;
}
/**
@@ -30,20 +30,17 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.ProtocolConfig;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.collections.ExpireHashMap;
import com.comphenix.protocol.error.Report.ReportBuilder;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.reflect.PrettyPrinter;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Primitives;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
/**
* Internal class used to handle exceptions.
@@ -472,7 +469,7 @@ public class DetailedErrorReporter implements ErrorReporter {
} if (isSimpleType(value) || value instanceof Class<?>) {
return value.toString();
} else {
try {
/*try {
if (!apacheCommonsMissing)
return ToStringBuilder.reflectionToString(value, ToStringStyle.MULTI_LINE_STYLE, false, null);
} catch (LinkageError ex) {
@@ -483,7 +480,7 @@ public class DetailedErrorReporter implements ErrorReporter {
} catch (Throwable ex) {
// Don't use the error logger to log errors in error logging (that could lead to infinite loops)
ProtocolLogger.log(Level.WARNING, "Cannot convert to a String with Apache: " + ex.getMessage());
}
}*/
// Use our custom object printer instead
try {
@@ -10,22 +10,8 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import javax.annotation.Nonnull;
import org.apache.commons.lang.Validate;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.WorldType;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.MerchantRecipe;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
@@ -55,7 +41,6 @@ import com.comphenix.protocol.wrappers.WrappedLevelChunkData;
import com.comphenix.protocol.wrappers.WrappedMessageSignature;
import com.comphenix.protocol.wrappers.WrappedNumberFormat;
import com.comphenix.protocol.wrappers.WrappedParticle;
import com.comphenix.protocol.wrappers.WrappedPositionMoveRotation;
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey;
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
import com.comphenix.protocol.wrappers.WrappedRegistrable;
@@ -66,11 +51,23 @@ import com.comphenix.protocol.wrappers.WrappedServerPing;
import com.comphenix.protocol.wrappers.WrappedStatistic;
import com.comphenix.protocol.wrappers.WrappedTeamParameters;
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
import com.comphenix.protocol.wrappers.WrappedWeightedList;
import com.comphenix.protocol.wrappers.nbt.NbtBase;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.google.common.base.Preconditions;
import com.comphenix.protocol.utility.Validate;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.WorldType;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.MerchantRecipe;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
public abstract class AbstractStructure {
protected transient Object handle;
@@ -420,16 +417,6 @@ public abstract class AbstractStructure {
BukkitConverters.getVectorConverter());
}
/**
* Retrieves a read/write structure for PositionMoveRotation fields.
* @return A modifier for PositionMoveRotation fields.
*/
public StructureModifier<WrappedPositionMoveRotation> getPositionMoveRotations() {
return structureModifier.withType(
MinecraftReflection.getMinecraftClass("world.entity.PositionMoveRotation"),
WrappedPositionMoveRotation.getConverter());
}
/**
* Retrieves a read/write structure for collections of attribute snapshots.
* <p>
@@ -609,7 +596,9 @@ public abstract class AbstractStructure {
*/
public StructureModifier<List<PlayerInfoData>> getPlayerInfoDataLists() {
// Convert to and from the ProtocolLib wrapper
return getLists(PlayerInfoData.getConverter());
return structureModifier.withType(
Collection.class,
BukkitConverters.getListConverter(PlayerInfoData.getConverter()));
}
/**
@@ -814,20 +803,6 @@ public abstract class AbstractStructure {
);
}
/**
* Retrieve a read/write structure for WeightedList fields.
*
* @param elementConverter converter for the element type inside each weighted entry
* @param <T> the Bukkit-side element type
* @return A modifier for WeightedList fields.
*/
public <T> StructureModifier<WrappedWeightedList<T>> getWeightedLists(
EquivalentConverter<T> elementConverter) {
return structureModifier.withType(
WrappedWeightedList.getNmsClass(),
WrappedWeightedList.getConverter(elementConverter));
}
/**
* Retrieve a read/write structure for the MobEffectList class in 1.9.
* @return A modifier for MobEffectList fields.
@@ -843,15 +818,6 @@ public abstract class AbstractStructure {
BukkitConverters.getEffectTypeConverter());
}
/**
* Retrieve a read/write structure for Bukkit {@code DamageType} values
* stored in NMS {@code Holder<DamageType>} fields.
* @return A modifier for Bukkit DamageType values.
*/
public StructureModifier<org.bukkit.damage.DamageType> getDamageTypes() {
return getHolders(MinecraftReflection.getDamageTypeClass(), BukkitConverters.getDamageTypeConverter());
}
/**
* Retrieve a read/write structure for the SoundCategory enum in 1.9.
* @return A modifier for SoundCategory enum fields.
@@ -1330,20 +1296,6 @@ public abstract class AbstractStructure {
));
}
/**
* Retrieve a read/write structure for {@link WrappedPositionMoveRotation} (available since Minecraft 1.21.2).
* <p>
* This is used in packets such as {@code ENTITY_TELEPORT} and {@code ENTITY_POSITION_SYNC}.
*
* @return A modifier for PositionMoveRotation fields.
*/
public StructureModifier<WrappedPositionMoveRotation> getPositionMoveRotation() {
return structureModifier.withType(
MinecraftReflection.getPositionMoveRotationClass(),
WrappedPositionMoveRotation.getConverter()
);
}
/**
* Represents an equivalent converter for ItemStack arrays.
* @author Kristian
@@ -232,13 +232,13 @@ public abstract class PacketAdapter implements PacketListener {
@Override
public void onPacketReceiving(PacketEvent event) {
// Lets prevent some bugs
throw new IllegalStateException("Override onPacketReceiving to get notifications of received packets!");
throw new IllegalStateException("Override onPacketReceiving to get notifcations of received packets!");
}
@Override
public void onPacketSending(PacketEvent event) {
// Lets prevent some bugs
throw new IllegalStateException("Override onPacketSending to get notifications of sent packets!");
throw new IllegalStateException("Override onPacketSending to get notifcations of sent packets!");
}
@Override
@@ -23,7 +23,7 @@ import java.util.concurrent.TimeUnit;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.apache.commons.lang.Validate;
import com.comphenix.protocol.utility.Validate;
/**
* Stores and retrieves metadata for applicable packet objects.
@@ -36,6 +36,7 @@ import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.ByteBuddyFactory;
import com.comphenix.protocol.utility.Util;
import io.papermc.paper.persistence.PersistentDataContainerView;
import net.bytebuddy.description.ByteCodeElement;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
@@ -60,7 +61,6 @@ import org.bukkit.World;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.profile.PlayerProfile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
@@ -157,11 +157,28 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
return null;
}
/**
* @param b
* @return
*/
@Override
public @org.jspecify.annotations.Nullable Location getRespawnLocation(boolean b) {
return null;
}
@Override
public Location getLocation() {
return null;
}
/**
* @return
*/
@Override
public PersistentDataContainerView getPersistentDataContainer() {
return null;
}
// TODO do we need to implement this?
public void incrementStatistic(Statistic statistic) throws IllegalArgumentException {
@@ -241,8 +258,11 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
return uuid;
}
/**
* @return
*/
@Override
public @NotNull PlayerProfile getPlayerProfile() {
public com.destroystokyo.paper.profile.PlayerProfile getPlayerProfile() {
return null;
}
@@ -288,6 +308,14 @@ class SerializedOfflinePlayer implements OfflinePlayer, Serializable {
return online;
}
/**
* @return
*/
@Override
public boolean isConnected() {
return false;
}
@Override
public boolean isWhitelisted() {
return whitelisted;
@@ -39,7 +39,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang.Validate;
import com.comphenix.protocol.utility.Validate;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
@@ -1,93 +0,0 @@
package com.comphenix.protocol.injector;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.reflect.EquivalentConverter;
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.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.Tuple;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
public class EquivalentConstructor {
private final PacketType packetType;
private final List<Tuple<Class<?>, Object>> converters = new ArrayList<>();
private volatile ConstructorAccessor constructorAccessor;
public EquivalentConstructor(PacketType packetType) {
this.packetType = packetType;
}
public EquivalentConstructor withParam(Class<?> param, EquivalentConverter<?> converter) {
converters.add(new Tuple<>(param, converter));
return this;
}
public EquivalentConstructor withParam(Class<?> param, PacketConstructor.Unwrapper unwrapper) {
converters.add(new Tuple<>(param, unwrapper));
return this;
}
public EquivalentConstructor withParam(Class<?> param) {
converters.add(new Tuple<>(param, null));
return this;
}
@SuppressWarnings({"unchecked", "rawtypes"})
public Object create(Object... args) {
if (args.length != converters.size()) {
throw new IllegalArgumentException("Expected " + converters.size() + " args, got " + args.length);
}
ConstructorAccessor accessor = getConstructorAccessor();
Object[] convertedArgs = new Object[converters.size()];
int i = 0;
for (Tuple<Class<?>, Object> entry : converters) {
Object rawConverter = entry.second();
if (rawConverter == null) {
convertedArgs[i] = args[i];
} else if (rawConverter instanceof EquivalentConverter converter) {
convertedArgs[i] = converter.getGeneric(args[i]);
} else if (rawConverter instanceof PacketConstructor.Unwrapper unwrapper) {
convertedArgs[i] = unwrapper.unwrapItem(args[i]);
} else {
throw new IllegalStateException("Invalid converter type: " + rawConverter.getClass());
}
i++;
}
return accessor.invoke(convertedArgs);
}
private ConstructorAccessor getConstructorAccessor() {
ConstructorAccessor accessor = constructorAccessor;
if (accessor == null) {
synchronized (this) {
accessor = constructorAccessor;
if (accessor != null) {
return accessor;
}
constructorAccessor = accessor = createConstructorAccessor();
}
}
return accessor;
}
private ConstructorAccessor createConstructorAccessor() {
Class<?>[] params = new Class<?>[converters.size()];
for (int i = 0; i < converters.size(); i++) {
params[i] = converters.get(i).first();
}
Constructor<?> ctor = FuzzyReflection.fromClass(packetType.getPacketClass(), true)
.getConstructor(FuzzyMethodContract.newBuilder().parameterExactArray(params).build());
return Accessors.getConstructorAccessor(ctor);
}
}
@@ -1,8 +1,6 @@
package com.comphenix.protocol.injector;
import com.comphenix.protocol.ProtocolManager;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
/**
* Yields access to the internal hook configuration.
@@ -11,14 +9,6 @@ import org.bukkit.plugin.PluginManager;
*/
public interface InternalManager extends ProtocolManager {
/**
* Register this protocol manager on Bukkit.
*
* @param manager - Bukkit plugin manager that provides player join/leave events.
* @param plugin - the parent plugin.
*/
void registerEvents(PluginManager manager, final Plugin plugin);
/**
* Called when ProtocolLib is closing.
*/
@@ -1,10 +1,12 @@
package com.comphenix.protocol.injector;
import javax.annotation.Nonnull;
import com.comphenix.protocol.ProtocolLib;
import com.comphenix.protocol.async.AsyncFilterManager;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.utility.MinecraftVersion;
import javax.annotation.Nonnull;
import org.bukkit.Server;
import org.bukkit.plugin.Plugin;
@@ -112,7 +114,7 @@ public class PacketFilterBuilder {
*
* @return A new packet filter manager.
*/
public InternalManager build() {
public PacketFilterManager build() {
if (this.reporter == null) {
throw new IllegalArgumentException("reporter cannot be NULL.");
}
@@ -8,21 +8,6 @@ import java.util.Objects;
import java.util.Set;
import java.util.logging.Level;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import com.comphenix.protocol.AsynchronousManager;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
@@ -43,12 +28,19 @@ import com.comphenix.protocol.injector.PluginVerifier.VerificationResult;
import com.comphenix.protocol.injector.collection.InboundPacketListenerSet;
import com.comphenix.protocol.injector.collection.OutboundPacketListenerSet;
import com.comphenix.protocol.injector.collection.PacketListenerSet;
import com.comphenix.protocol.injector.netty.Injector;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.comphenix.protocol.injector.netty.manager.NetworkManagerInjector;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.collect.ImmutableSet;
import io.netty.channel.Channel;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
public class PacketFilterManager implements ListenerManager, InternalManager {
@@ -152,20 +144,29 @@ public class PacketFilterManager implements ListenerManager, InternalManager {
@Override
public void sendServerPacket(Player receiver, PacketContainer packet, NetworkMarker marker, boolean filters) {
if (this.closed) {
return;
}
// A monitor listener should never modify a packet/event so we can simply invoke monitor listeners
// independently of our injector pipeline
if (!filters) {
this.runMonitorListeners(packet, () -> {
if (!this.closed) {
// if we skip the packet events later when actually writing into the pipeline we at least notify all
// monitor listeners before doing so - they will not be able to change the event tho
if (!filters) {
// ensure we are on the main thread if any listener requires that
if (this.hasMainThreadListener(packet.getType()) && !this.server.isPrimaryThread()) {
NetworkMarker copy = marker; // okay fine
ProtocolLibrary.getScheduler().scheduleSyncDelayedTask(
() -> this.sendServerPacket(receiver, packet, copy, false), 1L);
return;
}
// construct the event and post to all monitor listeners
PacketEvent event = PacketEvent.fromServer(this, packet, marker, receiver, false);
this.outboundListeners.invoke(event, ListenerPriority.MONITOR);
});
}
this.networkManagerInjector.getInjector(receiver).sendClientboundPacket(packet.getHandle(), marker, filters);
// update the marker of the event without accidentally constructing it
marker = NetworkMarker.getNetworkMarker(event);
}
// process outbound
this.networkManagerInjector.getInjector(receiver).sendClientboundPacket(packet.getHandle(), marker, filters);
}
}
@Override
@@ -192,40 +193,34 @@ public class PacketFilterManager implements ListenerManager, InternalManager {
@Override
public void receiveClientPacket(Player sender, PacketContainer packet, NetworkMarker marker, boolean filters) {
if (this.closed) {
return;
}
// make sure we are on the main thread if any listener of the packet needs it
if (filters && this.requiresMainThread(packet)) {
ProtocolLibrary.getScheduler().runTask(
() -> this.receiveClientPacket(sender, packet, marker, filters));
return;
}
Object nmsPacket = packet.getHandle();
if (filters) {
PacketEvent event = PacketEvent.fromClient(this, packet, marker, sender, false);
this.invokeInboundPacketListeners(event);
if (event.isCancelled()) {
if (!this.closed) {
// make sure we are on the main thread if any listener of the packet needs it
if (this.hasMainThreadListener(packet.getType()) && !this.server.isPrimaryThread()) {
ProtocolLibrary.getScheduler().runTask(
() -> this.receiveClientPacket(sender, packet, marker, filters));
return;
}
// Prevent possible de-sync if the packet was replaced by a listener.
nmsPacket = event.getPacket().getHandle();
} else {
// A monitor listener should never modify a packet/event so we can simply invoke monitor listeners
// independently of our injector pipeline
this.runMonitorListeners(packet, () -> {
Object nmsPacket = packet.getHandle();
// check to which listeners we need to post the packet
if (filters) {
// post to all listeners
PacketEvent event = PacketEvent.fromClient(this.networkManagerInjector, packet, null, sender);
this.invokeInboundPacketListeners(event);
if (event.isCancelled()) {
return;
}
// prevent possible de-sync
nmsPacket = event.getPacket().getHandle();
} else {
PacketEvent event = PacketEvent.fromClient(this, packet, marker, sender, false);
this.inboundListeners.invoke(event, ListenerPriority.MONITOR);
});
}
}
// post to the player inject, reset our cancel state change
this.networkManagerInjector.getInjector(sender).readServerboundPacket(nmsPacket);
// post to the player inject, reset our cancel state change
this.networkManagerInjector.getInjector(sender).readServerboundPacket(nmsPacket);
}
}
@Override
@@ -325,24 +320,30 @@ public class PacketFilterManager implements ListenerManager, InternalManager {
@Override
public void removePacketListener(PacketListener listener) {
if (!this.closed && this.registeredListeners.remove(listener)) {
ListeningWhitelist outbound = listener.getSendingWhitelist();
ListeningWhitelist inbound = listener.getReceivingWhitelist();
if (this.closed || !this.registeredListeners.remove(listener)) {
return;
}
// remove outbound listeners (if any)
if (outbound != null && outbound.isEnabled()) {
this.outboundListeners.removeListener(listener);
}
ListeningWhitelist outbound = listener.getSendingWhitelist();
ListeningWhitelist inbound = listener.getReceivingWhitelist();
// remove inbound listeners (if any)
if (inbound != null && inbound.isEnabled()) {
this.inboundListeners.removeListener(listener);
}
// remove outbound listeners (if any)
if (outbound != null && outbound.isEnabled()) {
this.outboundListeners.removeListener(listener);
}
// remove inbound listeners (if any)
if (inbound != null && inbound.isEnabled()) {
this.inboundListeners.removeListener(listener);
}
}
@Override
public void removePacketListeners(Plugin plugin) {
if (this.closed || this.plugin.equals(plugin)) {
return;
}
for (PacketListener listener : this.getPacketListeners()) {
if (Objects.equals(listener.getPlugin(), plugin)) {
this.removePacketListener(listener);
@@ -419,40 +420,26 @@ public class PacketFilterManager implements ListenerManager, InternalManager {
}
}
@Override
public void registerEvents(PluginManager manager, Plugin plugin) {
if (!this.closed && !this.injected) {
// prevent duplicate event registrations / injections
this.injected = true;
this.networkManagerInjector.inject();
public void removePlayer(Player player) {
asyncFilterManager.removePlayer(player);
}
// all listeners we need, this is a bit messy, but it makes the job correctly
manager.registerEvents(new Listener() {
public void injectPlayer(Player player) {
networkManagerInjector.getInjector(player).inject();
}
@EventHandler(priority = EventPriority.LOWEST)
public void handleLogin(PlayerLoginEvent event) {
networkManagerInjector.getInjector(event.getPlayer()).inject();
}
public void injectChannel(Channel channel) {
networkManagerInjector.getInjector(channel).inject();
}
@EventHandler(priority = EventPriority.LOWEST)
public void handleJoin(PlayerJoinEvent event) {
networkManagerInjector.getInjector(event.getPlayer()).inject();
}
@EventHandler(priority = EventPriority.MONITOR)
public void handleQuit(PlayerQuitEvent event) {
PacketFilterManager.this.asyncFilterManager.removePlayer(event.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR)
public void handlePluginUnload(PluginDisableEvent event) {
// don't do this for our plugin!
if (event.getPlugin() != PacketFilterManager.this.plugin) {
PacketFilterManager.this.removePacketListeners(event.getPlugin());
}
}
}, plugin);
public void inject() {
if (this.closed || this.injected) {
return;
}
// prevent duplicate event registrations / injections
this.injected = true;
this.networkManagerInjector.inject();
}
@Override
@@ -514,25 +501,12 @@ public class PacketFilterManager implements ListenerManager, InternalManager {
this.postPacketToListeners(this.outboundListeners, event, true);
}
}
private boolean requiresMainThread(PacketContainer packet) {
return this.hasMainThreadListener(packet.getType()) && !this.server.isPrimaryThread();
}
private void runMonitorListeners(PacketContainer packet, Runnable notifyMonitor) {
if (this.requiresMainThread(packet)) {
ProtocolLibrary.getScheduler().runTask(notifyMonitor);
} else {
notifyMonitor.run();
}
}
private void postPacketToListeners(PacketListenerSet listeners, PacketEvent event, boolean outbound) {
try {
// append async marker if any async listener for the packet was registered
if (this.asyncFilterManager.hasAsynchronousListeners(event)) {
Injector injector = this.networkManagerInjector.getInjector(event.getPlayer());
event.setAsyncMarker(this.asyncFilterManager.createAsyncMarker(injector));
event.setAsyncMarker(this.asyncFilterManager.createAsyncMarker());
}
// post to sync listeners
@@ -287,8 +287,8 @@ public class StructureCache {
if (registryByteBuf.isPresent()) {
ConstructorAccessor accessor = Accessors.getConstructorAccessor(FuzzyReflection.fromClass(serializerBase, true).getConstructor(FuzzyMethodContract.newBuilder()
.parameterExactType(ByteBuf.class)
.parameterExactType(MinecraftReflection.getRegistryAccessClass())
.parameterDerivedOf(ByteBuf.class)
.parameterDerivedOf(MinecraftReflection.getRegistryAccessClass())
.build()));
TRICKED_DATA_SERIALIZER_BASE = (buf) -> accessor.invoke(buf, MinecraftRegistryAccess.get());
} else {
@@ -58,13 +58,11 @@ public class OutboundPacketListenerSet extends PacketListenerSet {
}
}
if (!event.isReadOnly()) {
if (packets.iterator().hasNext()) {
event.getPacket().getPacketBundles().write(0, outPackets);
} else {
// cancel entire packet if each individual packet has been cancelled
event.setCancelled(true);
}
if (packets.iterator().hasNext()) {
event.getPacket().getPacketBundles().write(0, outPackets);
} else {
// cancel entire packet if each individual packet has been cancelled
event.setCancelled(true);
}
}
}
@@ -2,12 +2,12 @@ package com.comphenix.protocol.injector.netty;
import java.net.SocketAddress;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.events.NetworkMarker;
import org.bukkit.entity.Player;
/**
* Represents an injected client connection.
*
@@ -74,6 +74,8 @@ public interface Injector {
*/
void setPlayer(Player player);
boolean hasValidPlayer();
boolean isConnected();
/**
@@ -2,13 +2,14 @@ package com.comphenix.protocol.injector.netty.channel;
import java.net.SocketAddress;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.events.NetworkMarker;
import com.comphenix.protocol.injector.netty.Injector;
import com.comphenix.protocol.injector.netty.WirePacket;
import com.comphenix.protocol.injector.temporary.TemporaryPlayer;
import org.bukkit.entity.Player;
final class EmptyInjector implements Injector {
@@ -72,6 +73,11 @@ final class EmptyInjector implements Injector {
this.player = player;
}
@Override
public boolean hasValidPlayer() {
return player != null && !(player instanceof TemporaryPlayer);
}
@Override
public boolean isConnected() {
return false;
@@ -16,12 +16,9 @@ package com.comphenix.protocol.injector.netty.channel;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentMap;
import javax.annotation.Nonnull;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.injector.ListenerManager;
import com.comphenix.protocol.injector.netty.Injector;
@@ -29,10 +26,12 @@ import com.comphenix.protocol.injector.temporary.TemporaryPlayerFactory;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.utility.MinecraftFields;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.MapMaker;
import com.google.common.collect.MapMaker;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
/**
* Represents an injector factory.
@@ -85,12 +84,8 @@ public class InjectionFactory {
// try to get the injector using the player reference first
Injector injector = this.playerLookup.get(player);
if (injector == null) {
injector = this.getTemporaryInjector(player);
}
// check if we found an injector
if (injector != null && !injector.isClosed()) {
ProtocolLogger.log(player.getName() + " has a cached injector. Player valid: " + injector.hasValidPlayer());
return injector;
}
@@ -110,7 +105,10 @@ public class InjectionFactory {
this.playerLookup.remove(injector.getPlayer());
this.cacheInjector(player, injector);
// re-set the player of the injection
ProtocolLogger.log("Reusing existing injector and setting player to " + player.getName());
injector.setPlayer(player);
} else {
ProtocolLogger.log(player.getName() + "'s channel already has an injector. Player valid: " + injector.hasValidPlayer());
}
} else {
// construct a new injector as it seems like we have none yet
@@ -11,9 +11,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicBoolean;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.ProtocolLibrary;
@@ -44,6 +41,8 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoop;
import io.netty.util.AttributeKey;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public class NettyChannelInjector implements Injector {
@@ -243,7 +242,7 @@ public class NettyChannelInjector implements Injector {
this.uninject();
// remove any outgoing references
this.channel.attr(INJECTOR).set(null);
this.channel.attr(INJECTOR).remove();
// cleanup
this.savedMarkers.clear();
@@ -350,9 +349,8 @@ public class NettyChannelInjector implements Injector {
// check if the name of the player is already known to the injector
if (this.playerName != null) {
Player bukkitPlayer = Bukkit.getPlayerExact(this.playerName);
if (bukkitPlayer != null) {
this.player = bukkitPlayer;
this.player = Bukkit.getPlayerExact(this.playerName);
if (this.player != null) {
this.injectionFactory.cacheInjector(this.player, this);
}
}
@@ -372,6 +370,11 @@ public class NettyChannelInjector implements Injector {
this.injectionFactory.cacheInjector(player.getName(), this);
}
@Override
public boolean hasValidPlayer() {
return this.player != null && !(player instanceof TemporaryPlayer);
}
@Override
public boolean isConnected() {
return this.channel.isActive();
@@ -5,7 +5,6 @@ import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelId;
import io.netty.channel.ChannelMetadata;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelProgressivePromise;
@@ -34,11 +33,6 @@ final class NettyChannelProxy implements Channel {
this.injector = injector;
}
@Override
public ChannelId id() {
return this.delegate.id();
}
@Override
public EventLoop eventLoop() {
return this.eventLoop;
@@ -244,11 +238,6 @@ final class NettyChannelProxy implements Channel {
return this.delegate.attr(key);
}
@Override
public <T> boolean hasAttr(AttributeKey<T> attributeKey) {
return this.delegate.hasAttr(attributeKey);
}
@Override
public int compareTo(@NotNull Channel o) {
return this.delegate.compareTo(o);
@@ -10,13 +10,11 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import io.netty.channel.AbstractEventLoop;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.util.concurrent.AbstractEventExecutor;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.ProgressivePromise;
@@ -27,7 +25,7 @@ import io.netty.util.concurrent.ScheduledFuture;
* An abstract event loop implementation which delegates all calls to a given event loop, but proxies all calls which
* schedule something on the event loop to methods which decide what should happen to the scheduled task.
*/
final class NettyEventLoopProxy extends AbstractEventLoop {
final class NettyEventLoopProxy implements EventLoop {
private static final Callable<?> EMPTY_CALLABLE = () -> null;
private static final Runnable EMPTY_RUNNABLE = () -> {
@@ -238,15 +236,6 @@ final class NettyEventLoopProxy extends AbstractEventLoop {
}
}
// ShreddedPaper uses this method
@Override
public void lazyExecute(Runnable command) {
Runnable proxied = this.proxyRunnable(command);
if (proxied != null) {
((AbstractEventExecutor)this.delegate).lazyExecute(proxied);
}
}
@Override
public void forEach(Consumer<? super EventExecutor> action) {
this.delegate.forEach(action);
@@ -262,12 +251,6 @@ final class NettyEventLoopProxy extends AbstractEventLoop {
return this.delegate.register(channel);
}
@Override
public ChannelFuture register(ChannelPromise channelPromise) {
return this.delegate.register(channelPromise);
}
@Deprecated
@Override
public ChannelFuture register(Channel channel, ChannelPromise promise) {
return this.delegate.register(channel, promise);
@@ -7,9 +7,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.error.ErrorReporter;
import com.comphenix.protocol.injector.ListenerManager;
@@ -23,7 +20,10 @@ import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.Pair;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
public class NetworkManagerInjector {
@@ -57,6 +57,10 @@ public class NetworkManagerInjector {
return this.injectionFactory.fromPlayer(player);
}
public Injector getInjector(Channel channel) {
return injectionFactory.fromChannel(channel);
}
@SuppressWarnings("unchecked")
public void inject() {
if (this.closed || this.injected) {
@@ -412,8 +412,8 @@ public class PacketRegistry {
private static Class<?> searchForPacket(List<String> classNames) {
for (String name : classNames) {
try {
Class<?> clazz = resolvePacketClassName(name);
if (clazz != null && MinecraftReflection.getPacketClass().isAssignableFrom(clazz)
Class<?> clazz = MinecraftReflection.getMinecraftClass(name);
if (MinecraftReflection.getPacketClass().isAssignableFrom(clazz)
&& !Modifier.isAbstract(clazz.getModifiers())) {
return clazz;
}
@@ -424,22 +424,6 @@ public class PacketRegistry {
return null;
}
/**
* Resolve a single packet class name. Fully-qualified names (those already containing their
* package, e.g. {@code net.minecraft.network.protocol.game.XPacket}) are loaded directly, since
* {@link MinecraftReflection#getMinecraftClass(String)} would otherwise prepend the Minecraft
* package and fail to find them. Relative names continue to be resolved against the Minecraft
* package.
*/
private static Class<?> resolvePacketClassName(String name) {
Optional<Class<?>> direct = MinecraftReflection.getOptionalClass(name);
if (direct.isPresent()) {
return direct.get();
}
return MinecraftReflection.getMinecraftClass(name);
}
/**
* Retrieves the correct packet class from a given type.
*
@@ -88,23 +88,20 @@ final class CachedPackage {
* @throws RuntimeException If we are unable to find the given class.
*/
public Optional<Class<?>> getPackageClass(String className, String... aliases) {
Optional<Class<?>> cached = cache.get(className);
if (cached != null) {
return cached;
}
return cache.computeIfAbsent(className, x -> {
Optional<Class<?>> clazz = resolveClass(className);
if (clazz.isPresent()) {
return clazz;
}
Optional<Class<?>> clazz = resolveClass(className);
if (!clazz.isPresent()) {
for (String alias : aliases) {
Optional<Class<?>> aliasClazz = resolveClass(alias);
if (aliasClazz.isPresent()) {
clazz = aliasClazz;
break;
clazz = resolveClass(alias);
if (clazz.isPresent()) {
return clazz;
}
}
}
cache.put(className, clazz);
return clazz;
return Optional.empty();
});
}
}
@@ -96,11 +96,6 @@ public final class MinecraftProtocolVersion {
map.put(new MinecraftVersion(1, 21, 2), 768);
map.put(new MinecraftVersion(1, 21, 6), 771);
map.put(new MinecraftVersion(1, 21, 8), 772);
map.put(new MinecraftVersion(1, 21, 9), 773);
map.put(new MinecraftVersion(1, 21, 11), 774);
map.put(new MinecraftVersion(26, 1, 0), 775);
map.put(new MinecraftVersion(26, 2, 0), 776);
return map;
}
@@ -553,10 +553,6 @@ public final class MinecraftReflection {
return getClass("com.mojang.authlib.GameProfile");
}
public static Class<?> getGameProfilePropertyMapClass() {
return getClass("com.mojang.authlib.properties.PropertyMap");
}
/**
* Retrieve the entity (NMS) class.
*
@@ -921,19 +917,15 @@ public final class MinecraftReflection {
}
public static Class<?> getMinecraftKeyClass() {
return getMinecraftClass("resources.MinecraftKey", "resources.Identifier", "resources.ResourceLocation", "MinecraftKey");
return getMinecraftClass("resources.MinecraftKey", "resources.ResourceLocation", "MinecraftKey");
}
public static Class<?> getMobEffectListClass() {
return getMinecraftClass("world.effect.MobEffectList", "MobEffectList", "world.effect.MobEffect", "world.effect.MobEffects");
}
public static Class<?> getDamageTypeClass() {
return getMinecraftClass("world.damagesource.DamageType");
return getMinecraftClass("world.effect.MobEffectList", "MobEffectList", "world.effect.MobEffect");
}
public static Class<?> getSoundEffectClass() {
return getNullableNMS("sounds.SoundEffect", "sounds.SoundEvent", "SoundEffect", "sounds.SoundEvents");
return getNullableNMS("sounds.SoundEffect", "sounds.SoundEvent", "SoundEffect");
}
/**
@@ -1122,15 +1114,6 @@ public final class MinecraftReflection {
return getOptionalNMS("network.chat.numbers.StyledFormat");
}
/**
* Retrieve the NMS TeamColor class, added in 26.2.
*
* @return The TeamColor class.
*/
public static Optional<Class<?>> getTeamColorClass() {
return getOptionalNMS("world.scores.TeamColor");
}
/**
* Retrieve the Gson class used by Minecraft.
*
@@ -1493,18 +1476,6 @@ public final class MinecraftReflection {
.orElseThrow(() -> new RuntimeException("Cannot find class " + className));
}
/**
* Attempt to load a class by its fully-qualified canonical name, without prepending the
* Minecraft package. Unlike {@link #getMinecraftClass(String)}, this can resolve names that
* already include their package (e.g. {@code net.minecraft.network.protocol.game.XPacket}).
*
* @param className - the fully-qualified class name.
* @return Optional that may contain the class.
*/
public static Optional<Class<?>> getOptionalClass(String className) {
return getClassSource().loadClass(className);
}
/**
* Retrieve the class object of a specific CraftBukkit class.
*
@@ -1709,7 +1680,7 @@ public final class MinecraftReflection {
}
public static Class<?> getEntityTypes() {
return getMinecraftClass("world.entity.EntityType", "world.entity.EntityTypes", "EntityTypes");
return getMinecraftClass("world.entity.EntityTypes", "world.entity.EntityType", "EntityTypes");
}
public static Class<?> getParticleParam() {
@@ -1929,12 +1900,15 @@ public final class MinecraftReflection {
return isMojangMapped;
}
/**
* Retrieves the PositionMoveRotation class (introduced in 1.21.2).
*
* @return The PositionMoveRotation class.
*/
public static Class<?> getPositionMoveRotationClass() {
return getMinecraftClass("world.entity.PositionMoveRotation");
public static Optional<Class<?>> getDataComponentMapClass() {
return getOptionalNMS("core.component.DataComponentMap");
}
public static Optional<Class<?>> getDataComponentHolderClass() {
return getOptionalNMS("core.component.DataComponentHolder");
}
public static Optional<Class<?>> getTypedDataComponentClass() {
return getOptionalNMS("core.component.TypedDataComponent");
}
}
@@ -36,31 +36,6 @@ import org.bukkit.Server;
* @author Kristian
*/
public final class MinecraftVersion implements Comparable<MinecraftVersion>, Serializable {
/**
* Version 26.2 - chaos cubed
*/
public static final MinecraftVersion v26_2 = new MinecraftVersion("26.2");
/**
* Version 26.1 - tiny takeover
*/
public static final MinecraftVersion v26_1 = new MinecraftVersion("26.1");
/**
* Version 1.21.11 - mounts of mayhem
*/
public static final MinecraftVersion v1_21_11 = new MinecraftVersion("1.21.11");
/**
* Version 1.21.10 - hotfix for 1.21.9
*/
public static final MinecraftVersion v1_21_10 = new MinecraftVersion("1.21.10");
/**
* Version 1.21.9 - the copper age drop
*/
public static final MinecraftVersion v1_21_9 = new MinecraftVersion("1.21.9");
/**
* Version 1.21.6 - chase the skies
*/
@@ -194,7 +169,7 @@ public final class MinecraftVersion implements Comparable<MinecraftVersion>, Ser
/**
* The latest release version of minecraft.
*/
public static final MinecraftVersion LATEST = v26_2;
public static final MinecraftVersion LATEST = v1_21_6;
// used when serializing
private static final long serialVersionUID = -8695133558996459770L;
@@ -1,3 +0,0 @@
package com.comphenix.protocol.utility;
public record Tuple<A, B>(A first, B second) {}
@@ -0,0 +1,17 @@
package com.comphenix.protocol.utility;
public final class Validate {
private Validate() {}
public static void notNull(Object obj, String err) {
if (obj == null) {
throw new IllegalArgumentException(err);
}
}
public static void isTrue(boolean bool, String err) {
if (!bool) {
throw new IllegalArgumentException(err);
}
}
}
@@ -0,0 +1,12 @@
package com.comphenix.protocol.utility;
public final class WordUtils {
private WordUtils() {}
public static String capitalize(String str) {
if (str == null || str.isEmpty()) {
return str;
}
return str.substring(0, 1).toUpperCase() + str.substring(1).toLowerCase();
}
}
@@ -2,14 +2,12 @@ package com.comphenix.protocol.utility;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.util.ByteProcessor;
import io.netty.buffer.ByteBufProcessor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel;
import java.nio.charset.Charset;
@@ -56,16 +54,6 @@ public class ZeroBuffer extends ByteBuf {
return false;
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public ByteBuf asReadOnly() {
return null;
}
@Override
public int readerIndex() {
return 0;
@@ -191,71 +179,36 @@ public class ZeroBuffer extends ByteBuf {
return 0;
}
@Override
public short getShortLE(int i) {
return 0;
}
@Override
public int getUnsignedShort(int i) {
return 0;
}
@Override
public int getUnsignedShortLE(int i) {
return 0;
}
@Override
public int getMedium(int i) {
return 0;
}
@Override
public int getMediumLE(int i) {
return 0;
}
@Override
public int getUnsignedMedium(int i) {
return 0;
}
@Override
public int getUnsignedMediumLE(int i) {
return 0;
}
@Override
public int getInt(int i) {
return 0;
}
@Override
public int getIntLE(int i) {
return 0;
}
@Override
public long getUnsignedInt(int i) {
return 0;
}
@Override
public long getUnsignedIntLE(int i) {
return 0;
}
@Override
public long getLong(int i) {
return 0;
}
@Override
public long getLongLE(int i) {
return 0;
}
@Override
public char getChar(int i) {
return 0;
@@ -311,16 +264,6 @@ public class ZeroBuffer extends ByteBuf {
return 0;
}
@Override
public int getBytes(int i, FileChannel fileChannel, long l, int i1) throws IOException {
return 0;
}
@Override
public CharSequence getCharSequence(int i, int i1, Charset charset) {
return "";
}
@Override
public ByteBuf setBoolean(int i, boolean b) {
return null;
@@ -336,41 +279,21 @@ public class ZeroBuffer extends ByteBuf {
return null;
}
@Override
public ByteBuf setShortLE(int i, int i1) {
return null;
}
@Override
public ByteBuf setMedium(int i, int i1) {
return null;
}
@Override
public ByteBuf setMediumLE(int i, int i1) {
return null;
}
@Override
public ByteBuf setInt(int i, int i1) {
return null;
}
@Override
public ByteBuf setIntLE(int i, int i1) {
return null;
}
@Override
public ByteBuf setLong(int i, long l) {
return null;
}
@Override
public ByteBuf setLongLE(int i, long l) {
return null;
}
@Override
public ByteBuf setChar(int i, int i1) {
return null;
@@ -426,21 +349,11 @@ public class ZeroBuffer extends ByteBuf {
return 0;
}
@Override
public int setBytes(int i, FileChannel fileChannel, long l, int i1) {
return 0;
}
@Override
public ByteBuf setZero(int i, int i1) {
return null;
}
@Override
public int setCharSequence(int i, CharSequence charSequence, Charset charset) {
return 0;
}
@Override
public boolean readBoolean() {
return false;
@@ -461,71 +374,36 @@ public class ZeroBuffer extends ByteBuf {
return 0;
}
@Override
public short readShortLE() {
return 0;
}
@Override
public int readUnsignedShort() {
return 0;
}
@Override
public int readUnsignedShortLE() {
return 0;
}
@Override
public int readMedium() {
return 0;
}
@Override
public int readMediumLE() {
return 0;
}
@Override
public int readUnsignedMedium() {
return 0;
}
@Override
public int readUnsignedMediumLE() {
return 0;
}
@Override
public int readInt() {
return 0;
}
@Override
public int readIntLE() {
return 0;
}
@Override
public long readUnsignedInt() {
return 0;
}
@Override
public long readUnsignedIntLE() {
return 0;
}
@Override
public long readLong() {
return 0;
}
@Override
public long readLongLE() {
return 0;
}
@Override
public char readChar() {
return 0;
@@ -551,11 +429,6 @@ public class ZeroBuffer extends ByteBuf {
return null;
}
@Override
public ByteBuf readRetainedSlice(int i) {
return null;
}
@Override
public ByteBuf readBytes(ByteBuf byteBuf) {
return null;
@@ -596,16 +469,6 @@ public class ZeroBuffer extends ByteBuf {
return 0;
}
@Override
public CharSequence readCharSequence(int i, Charset charset) {
return "";
}
@Override
public int readBytes(FileChannel fileChannel, long l, int i) {
return 0;
}
@Override
public ByteBuf skipBytes(int i) {
return null;
@@ -626,41 +489,21 @@ public class ZeroBuffer extends ByteBuf {
return null;
}
@Override
public ByteBuf writeShortLE(int i) {
return null;
}
@Override
public ByteBuf writeMedium(int i) {
return null;
}
@Override
public ByteBuf writeMediumLE(int i) {
return null;
}
@Override
public ByteBuf writeInt(int i) {
return null;
}
@Override
public ByteBuf writeIntLE(int i) {
return null;
}
@Override
public ByteBuf writeLong(long l) {
return null;
}
@Override
public ByteBuf writeLongLE(long l) {
return null;
}
@Override
public ByteBuf writeChar(int i) {
return null;
@@ -716,21 +559,11 @@ public class ZeroBuffer extends ByteBuf {
return 0;
}
@Override
public int writeBytes(FileChannel fileChannel, long l, int i) {
return 0;
}
@Override
public ByteBuf writeZero(int i) {
return null;
}
@Override
public int writeCharSequence(CharSequence charSequence, Charset charset) {
return 0;
}
@Override
public int indexOf(int i, int i1, byte b) {
return 0;
@@ -752,22 +585,22 @@ public class ZeroBuffer extends ByteBuf {
}
@Override
public int forEachByte(ByteProcessor byteProcessor) {
public int forEachByte(ByteBufProcessor byteBufProcessor) {
return 0;
}
@Override
public int forEachByte(int i, int i1, ByteProcessor byteProcessor) {
public int forEachByte(int i, int i1, ByteBufProcessor byteBufProcessor) {
return 0;
}
@Override
public int forEachByteDesc(ByteProcessor byteProcessor) {
public int forEachByteDesc(ByteBufProcessor byteBufProcessor) {
return 0;
}
@Override
public int forEachByteDesc(int i, int i1, ByteProcessor byteProcessor) {
public int forEachByteDesc(int i, int i1, ByteBufProcessor byteBufProcessor) {
return 0;
}
@@ -786,31 +619,16 @@ public class ZeroBuffer extends ByteBuf {
return null;
}
@Override
public ByteBuf retainedSlice() {
return null;
}
@Override
public ByteBuf slice(int i, int i1) {
return null;
}
@Override
public ByteBuf retainedSlice(int i, int i1) {
return null;
}
@Override
public ByteBuf duplicate() {
return null;
}
@Override
public ByteBuf retainedDuplicate() {
return null;
}
@Override
public int nioBufferCount() {
return 0;
@@ -920,14 +738,4 @@ public class ZeroBuffer extends ByteBuf {
public ByteBuf retain() {
return null;
}
@Override
public ByteBuf touch() {
return null;
}
@Override
public ByteBuf touch(Object o) {
return null;
}
}
@@ -22,7 +22,6 @@ import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.google.common.base.Defaults;
import com.google.common.base.Preconditions;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
@@ -166,67 +165,16 @@ public class AutoWrapper<T> implements EquivalentConverter<T> {
nmsInstanceCreator = noArgs;
nmsDefaultArgs = NO_ARGS;
} else {
// No no-arg constructor. For records (and most NMS data classes) the canonical
// constructor has one parameter per non-static field; that's the one we want.
// Other constructors (e.g. FriendlyByteBuf decoders) take a single network-buffer
// argument and would call methods on it during construction, so they're unsafe to
// invoke with default values.
Constructor<?>[] ctors = nmsClass.getDeclaredConstructors();
Constructor<?> chosen = selectConstructor(ctors);
nmsInstanceCreator = Accessors.getConstructorAccessor(chosen);
Class<?>[] paramTypes = chosen.getParameterTypes();
nmsDefaultArgs = new Object[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
nmsDefaultArgs[i] = paramTypes[i].isPrimitive()
? Defaults.defaultValue(paramTypes[i])
: null;
}
// use the first constructor of the class
nmsInstanceCreator = Accessors.getConstructorAccessor(nmsClass.getDeclaredConstructors()[0]);
nmsDefaultArgs = Arrays
.stream(nmsInstanceCreator.getConstructor().getParameterTypes())
.map(type -> type.isPrimitive() ? Defaults.defaultValue(type) : null)
.toArray(Object[]::new);
}
}
}
private Constructor<?> selectConstructor(Constructor<?>[] ctors) {
Constructor<?> fallback = null;
for (Constructor<?> ctor : ctors) {
if (isNetworkBufferConstructor(ctor)) {
continue;
}
if (ctor.getParameterCount() == nmsAccessors.length) {
return ctor;
}
if (fallback == null || ctor.getParameterCount() > fallback.getParameterCount()) {
fallback = ctor;
}
}
if (fallback != null) {
return fallback;
}
throw new InvalidWrapperException(nmsClass.getSimpleName()
+ " has no safe constructor for default instantiation", null);
}
private boolean isNetworkBufferConstructor(Constructor<?> ctor) {
Class<?> packetDataSerializer = MinecraftReflection.getPacketDataSerializerClass();
Class<?> registryByteBuf = MinecraftReflection.getRegistryFriendlyByteBufClass().orElse(null);
for (Class<?> param : ctor.getParameterTypes()) {
// Only treat the parameter as a network buffer if it's the buffer type itself or a subtype.
// Using the reverse direction (param.isAssignableFrom(...)) would also exclude legitimate
// ctors taking ByteBuf / Object / etc. as record components.
if (packetDataSerializer.isAssignableFrom(param)) {
return true;
}
if (registryByteBuf != null && registryByteBuf.isAssignableFrom(param)) {
return true;
}
}
return false;
}
// ---- Equivalent conversion
@Override
@@ -70,7 +70,6 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.WorldType;
@@ -874,13 +873,6 @@ public class BukkitConverters {
return ignoreNull(new EquivalentConverter<EntityType>() {
@Override
public Object getGeneric(EntityType specific) {
if (MinecraftVersion.v26_2.atOrAbove()) {
// EntityType.byString was removed in 26.2, resolve through the registry instead
WrappedRegistry registry = WrappedRegistry.getRegistry(MinecraftReflection.getEntityTypes());
NamespacedKey key = specific.getKey();
return registry.get(new MinecraftKey(key.getNamespace(), key.getKey()));
}
if (entityTypeFromName == null) {
Class<?> entityTypesClass = MinecraftReflection.getEntityTypes();
entityTypeFromName = Accessors.getMethodAccessor(
@@ -1436,67 +1428,6 @@ public class BukkitConverters {
});
}
// Lazily-resolved CraftDamageType reflection. volatile + DCL keeps the publish safe
// and avoids re-running fuzzy lookups in concurrent injection paths.
private static volatile MethodAccessor damageTypeToNms = null;
private static volatile MethodAccessor damageTypeFromNms = null;
private static FuzzyReflection craftDamageTypeFuzzy() {
return FuzzyReflection.fromClass(getCraftBukkitClass("damage.CraftDamageType"), false);
}
private static MethodAccessor resolveDamageTypeToNms() {
MethodAccessor accessor = damageTypeToNms;
if (accessor != null) return accessor;
synchronized (BukkitConverters.class) {
accessor = damageTypeToNms;
if (accessor != null) return accessor;
accessor = Accessors.getMethodAccessor(craftDamageTypeFuzzy().getMethod(FuzzyMethodContract.newBuilder()
.parameterExactArray(org.bukkit.damage.DamageType.class)
.returnTypeExact(MinecraftReflection.getDamageTypeClass())
.requireModifier(Modifier.STATIC)
.build()));
damageTypeToNms = accessor;
return accessor;
}
}
private static MethodAccessor resolveDamageTypeFromNms() {
MethodAccessor accessor = damageTypeFromNms;
if (accessor != null) return accessor;
synchronized (BukkitConverters.class) {
accessor = damageTypeFromNms;
if (accessor != null) return accessor;
accessor = Accessors.getMethodAccessor(craftDamageTypeFuzzy().getMethod(FuzzyMethodContract.newBuilder()
.parameterExactArray(MinecraftReflection.getDamageTypeClass())
.returnTypeExact(org.bukkit.damage.DamageType.class)
.requireModifier(Modifier.STATIC)
.build()));
damageTypeFromNms = accessor;
return accessor;
}
}
public static EquivalentConverter<org.bukkit.damage.DamageType> getDamageTypeConverter() {
return ignoreNull(new EquivalentConverter<org.bukkit.damage.DamageType>() {
@Override
public Class<org.bukkit.damage.DamageType> getSpecificType() {
return org.bukkit.damage.DamageType.class;
}
@Override
public Object getGeneric(org.bukkit.damage.DamageType specific) {
return resolveDamageTypeToNms().invoke(null, specific);
}
@Override
public org.bukkit.damage.DamageType getSpecific(Object generic) {
return (org.bukkit.damage.DamageType) resolveDamageTypeFromNms().invoke(null, generic);
}
});
}
private static Class<?> dimensionManager;
private static FauxEnumConverter<Dimension> dimensionConverter;
private static FauxEnumConverter<DimensionImpl> dimensionImplConverter;
@@ -46,13 +46,8 @@ public final class CustomPacketPayloadWrapper {
private static final ConstructorAccessor PAYLOAD_WRAPPER_CONSTRUCTOR;
private static final MethodAccessor GET_ID_PAYLOAD_METHOD;
private static final MethodAccessor GET_TYPE_PAYLOAD_METHOD;
private static final MethodAccessor GET_ID_PAYLOAD_TYPE_METHOD;
private static final MethodAccessor SERIALIZE_PAYLOAD_METHOD;
/** True when running on MC 1.21.5+ where DiscardedPayload is used instead of the ByteBuddy proxy. */
private static final boolean USE_DISCARDED_PAYLOAD;
private static final EquivalentConverter<CustomPacketPayloadWrapper> CONVERTER;
static {
@@ -60,54 +55,22 @@ public final class CustomPacketPayloadWrapper {
MINECRAFT_KEY_CLASS = MinecraftReflection.getMinecraftKeyClass();
CUSTOM_PACKET_PAYLOAD_CLASS = MinecraftReflection.getMinecraftClass("network.protocol.common.custom.CustomPacketPayload");
// MC 1.21.5+: CustomPacketPayload no longer has id(); DiscardedPayload is the fallback payload
// MC < 1.21.5: CustomPacketPayload.id() returns Identifier directly
Method getPayloadId = null;
Method getPayloadType = null;
Method getPayloadTypeId = null;
boolean useDiscardedPayload = false;
try {
getPayloadId = FuzzyReflection.fromClass(CUSTOM_PACKET_PAYLOAD_CLASS).getMethod(FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.returnTypeExact(MINECRAFT_KEY_CLASS)
.parameterCount(0)
.build());
} catch (IllegalArgumentException ignored) {
// New API: CustomPacketPayload.type().id() exposes the identifier for every payload type.
getPayloadType = CUSTOM_PACKET_PAYLOAD_CLASS.getMethod("type");
getPayloadTypeId = getPayloadType.getReturnType().getMethod("id");
useDiscardedPayload = true;
}
GET_ID_PAYLOAD_METHOD = getPayloadId != null ? Accessors.getMethodAccessor(getPayloadId) : null;
GET_TYPE_PAYLOAD_METHOD = getPayloadType != null ? Accessors.getMethodAccessor(getPayloadType) : null;
GET_ID_PAYLOAD_TYPE_METHOD = getPayloadTypeId != null ? Accessors.getMethodAccessor(getPayloadTypeId) : null;
USE_DISCARDED_PAYLOAD = useDiscardedPayload;
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);
// serialize method may not exist in new API
Method serializePayloadData = null;
try {
serializePayloadData = FuzzyReflection.fromClass(CUSTOM_PACKET_PAYLOAD_CLASS).getMethod(FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.returnTypeVoid()
.parameterCount(1)
.parameterDerivedOf(ByteBuf.class, 0)
.build());
} catch (IllegalArgumentException ignored) {
// Not present in new API
}
SERIALIZE_PAYLOAD_METHOD = serializePayloadData != null
? Accessors.getMethodAccessor(serializePayloadData) : null;
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;
if (useDiscardedPayload) {
// New API: use DiscardedPayload(Identifier, ByteBuf) as the payload handle
Class<?> discardedPayloadClass = MinecraftReflection.getMinecraftClass(
"network.protocol.common.custom.DiscardedPayload");
payloadWrapperConstructor = discardedPayloadClass.getConstructor(
MINECRAFT_KEY_CLASS, ByteBuf.class);
} else {
payloadWrapperConstructor = makePayloadWrapper();
}
Constructor<?> payloadWrapperConstructor = makePayloadWrapper();
PAYLOAD_WRAPPER_CONSTRUCTOR = Accessors.getConstructorAccessor(payloadWrapperConstructor);
CONVERTER = new EquivalentConverter<CustomPacketPayloadWrapper>() {
@@ -208,7 +171,8 @@ public final class CustomPacketPayloadWrapper {
* @return a wrapper holding the minecraft key and payload of the given custom payload instance.
*/
public static CustomPacketPayloadWrapper fromUnknownPayload(Object payload) {
MinecraftKey id = getPayloadId(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
@@ -221,12 +185,6 @@ public final class CustomPacketPayloadWrapper {
return data;
})
.orElseGet(() -> {
if (SERIALIZE_PAYLOAD_METHOD == null) {
throw new UnsupportedOperationException(
"Cannot extract raw custom payload bytes from " + payload.getClass().getName()
+ ": payload has no ByteBuf field and this Minecraft version does not "
+ "expose CustomPacketPayload serialization");
}
ByteBuf buffer = Unpooled.buffer();
Object serializer = MinecraftReflection.getPacketDataSerializer(buffer);
SERIALIZE_PAYLOAD_METHOD.invoke(payload, serializer);
@@ -236,17 +194,6 @@ public final class CustomPacketPayloadWrapper {
return new CustomPacketPayloadWrapper(messagePayload, id);
}
private static MinecraftKey getPayloadId(Object payload) {
Object messageId;
if (GET_ID_PAYLOAD_METHOD != null) {
messageId = GET_ID_PAYLOAD_METHOD.invoke(payload);
} else {
Object payloadType = GET_TYPE_PAYLOAD_METHOD.invoke(payload);
messageId = GET_ID_PAYLOAD_TYPE_METHOD.invoke(payloadType);
}
return MinecraftKey.getConverter().getSpecific(messageId);
}
/**
* Get the generic id of the wrapped message id.
*
@@ -283,11 +230,6 @@ public final class CustomPacketPayloadWrapper {
* @return a new payload wrapper instance using the provided message id and payload.
*/
public Object newHandle() {
if (USE_DISCARDED_PAYLOAD) {
// New API: DiscardedPayload(Identifier, ByteBuf)
ByteBuf buf = Unpooled.copiedBuffer(this.payload);
return PAYLOAD_WRAPPER_CONSTRUCTOR.invoke(this.getGenericId(), buf);
}
return PAYLOAD_WRAPPER_CONSTRUCTOR.invoke(this.getGenericId(), this.payload);
}
@@ -11,7 +11,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang.Validate;
import com.comphenix.protocol.utility.Validate;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
@@ -36,12 +36,6 @@ public abstract class EnumWrappers {
public enum ClientCommand {
PERFORM_RESPAWN,
REQUEST_STATS,
REQUEST_GAMERULE_VALUES,
/**
* @deprecated Removed in 26.1
*/
@Deprecated
OPEN_INVENTORY_ACHIEVEMENT
}
@@ -186,8 +180,7 @@ public abstract class EnumWrappers {
DROP_ALL_ITEMS,
DROP_ITEM,
RELEASE_USE_ITEM,
SWAP_HELD_ITEMS("SWAP_ITEM_WITH_OFFHAND"),
STAB;
SWAP_HELD_ITEMS("SWAP_ITEM_WITH_OFFHAND");
final String[] aliases;
PlayerDigType(String... aliases) {
@@ -724,11 +717,6 @@ public abstract class EnumWrappers {
MOTION_BLOCKING_NO_LEAVES,
}
public enum RelativeArgument {
// Order must match the NMS Relative enum exactly.
X, Y, Z, Y_ROT, X_ROT, DELTA_X, DELTA_Y, DELTA_Z, ROTATE_DELTA
}
private static Class<?> PROTOCOL_CLASS = null;
private static Class<?> CLIENT_COMMAND_CLASS = null;
private static Class<?> CHAT_VISIBILITY_CLASS = null;
@@ -757,7 +745,6 @@ public abstract class EnumWrappers {
private static Class<?> TEAM_COLLISION_RULE_CLASS = null;
private static Class<?> TEAM_VISIBILITY_CLASS = null;
private static Class<?> HEIGHTMAP_TYPE_CLASS = null;
private static Class<?> RELATIVE_CLASS = null;
private static boolean INITIALIZING = false;
private static boolean INITIALIZED = false;
@@ -818,7 +805,7 @@ public abstract class EnumWrappers {
}
try {
SOUND_CATEGORY_CLASS = MinecraftReflection.getMinecraftClass("sounds.SoundCategory", "sounds.SoundSource");
SOUND_CATEGORY_CLASS = MinecraftReflection.getMinecraftClass("sounds.SoundCategory");
} catch (Exception ex) {
SOUND_CATEGORY_CLASS = getEnum(PacketType.Play.Server.NAMED_SOUND_EFFECT.getPacketClass(), 0);
}
@@ -834,17 +821,13 @@ public abstract class EnumWrappers {
if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove()) {
HAND_CLASS = MinecraftReflection.getMinecraftClass("world.EnumHand", "world.InteractionHand");
if (MinecraftVersion.v26_1.atOrAbove()) {
// TODO
} else {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getEnumEntityUseActionClass(), true);
Method getType = fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterCount(0)
.returnTypeMatches(FuzzyMatchers.except(Void.class))
.build());
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getEnumEntityUseActionClass(), true);
Method getType = fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterCount(0)
.returnTypeMatches(FuzzyMatchers.except(Void.class))
.build());
ENTITY_USE_ACTION_CLASS = getType.getReturnType();
}
ENTITY_USE_ACTION_CLASS = getType.getReturnType();
} else {
HAND_CLASS = getEnum(PacketType.Play.Client.USE_ENTITY.getPacketClass(), 1);
ENTITY_USE_ACTION_CLASS = getEnum(PacketType.Play.Client.USE_ENTITY.getPacketClass(), 0);
@@ -880,8 +863,6 @@ public abstract class EnumWrappers {
"world.level.levelgen.HeightMap$Type" /* Spigot Mapping */,
"world.level.levelgen.Heightmap$Types" /* Mojang Mapping */);
RELATIVE_CLASS = MinecraftReflection.getMinecraftClass("world.entity.Relative");
associate(PROTOCOL_CLASS, Protocol.class, getProtocolConverter());
associate(CLIENT_COMMAND_CLASS, ClientCommand.class, getClientCommandConverter());
associate(CHAT_VISIBILITY_CLASS, ChatVisibility.class, getChatVisibilityConverter());
@@ -909,7 +890,6 @@ public abstract class EnumWrappers {
associate(TEAM_COLLISION_RULE_CLASS, TeamCollisionRule.class, getTeamCollisionRuleConverter());
associate(TEAM_VISIBILITY_CLASS, TeamVisibility.class, getTeamVisibilityConverter());
associate(HEIGHTMAP_TYPE_CLASS, HeightmapType.class, getHeightmapTypeConverter());
associate(RELATIVE_CLASS, RelativeArgument.class, getRelativeArgumentConverter());
if (ENTITY_POSE_CLASS != null) {
associate(ENTITY_POSE_CLASS, EntityPose.class, getEntityPoseConverter());
@@ -1099,11 +1079,6 @@ public abstract class EnumWrappers {
return HEIGHTMAP_TYPE_CLASS;
}
public static Class<?> getRelativeArgumentClass() {
initialize();
return RELATIVE_CLASS;
}
// Get the converters
public static EquivalentConverter<Protocol> getProtocolConverter() {
return new EnumConverter<>(getProtocolClass(), Protocol.class);
@@ -1213,10 +1188,6 @@ public abstract class EnumWrappers {
return new EnumConverter<>(getHeightmapTypeClass(), HeightmapType.class);
}
public static EquivalentConverter<RelativeArgument> getRelativeArgumentConverter() {
return new EnumConverter<>(getRelativeArgumentClass(), RelativeArgument.class);
}
/**
* @since 1.13+
* @return {@link EnumConverter} or null (if bellow 1.13 / nms EnumPose class cannot be found)
@@ -37,7 +37,7 @@ import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
import com.comphenix.protocol.wrappers.collection.ConvertedMap;
import org.apache.commons.lang.Validate;
import com.comphenix.protocol.utility.Validate;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
@@ -53,7 +53,14 @@ public class MinecraftKey {
* @param key The key
*/
public MinecraftKey(String key) {
this("minecraft", key);
if (key.contains(":")) {
String[] split = key.split(":", 2);
this.prefix = split[0];
this.key = split[1];
} else {
this.prefix = "minecraft";
this.key = key;
}
}
/**
@@ -1,29 +0,0 @@
package com.comphenix.protocol.wrappers;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.properties.PropertyMap;
/**
* Mutable version of PropertyMap
* Credit to PaperMC: https://github.com/PaperMC/Paper/blob/main/paper-server/src/main/java/io/papermc/paper/profile/MutablePropertyMap.java
*/
public class MutablePropertyMap extends PropertyMap {
private final Multimap<String, Property> properties = HashMultimap.create();
public MutablePropertyMap() {
super(ImmutableMultimap.of());
}
public MutablePropertyMap(final Multimap<String, Property> properties) {
this();
this.putAll(properties);
}
@Override
protected Multimap<String, Property> delegate() {
return this.properties;
}
}
@@ -0,0 +1,45 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.utility.MinecraftReflection;
public class ResourceKey {
private static final AutoWrapper<ResourceKey> AUTO_WRAPPER = AutoWrapper.wrap(ResourceKey.class, MinecraftReflection.getResourceKey())
.field(0, MinecraftKey.getConverter())
.field(1, MinecraftKey.getConverter());
MinecraftKey registryName;
MinecraftKey location;
ResourceKey() {
}
public ResourceKey(MinecraftKey registryName, MinecraftKey location) {
this.registryName = registryName;
this.location = location;
}
public MinecraftKey getRegistryName() {
return registryName;
}
public MinecraftKey getLocation() {
return location;
}
public void setRegistryName(MinecraftKey registryName) {
this.registryName = registryName;
}
public void setLocation(MinecraftKey location) {
this.location = location;
}
public static ResourceKey fromGeneric(Object generic) {
return AUTO_WRAPPER.wrap(generic);
}
public Object toGeneric() {
return AUTO_WRAPPER.unwrap(this);
}
}
@@ -1,119 +0,0 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import java.util.Objects;
import java.util.Optional;
/**
* Wrapper for {@code ChatType.Bound}.
*/
public final class WrappedChatTypeBound {
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getMinecraftClass("network.chat.ChatType$Bound");
private static final ConstructorAccessor CONSTRUCTOR = Accessors.getConstructorAccessor(
HANDLE_TYPE,
MinecraftReflection.getHolderClass(),
MinecraftReflection.getIChatBaseComponentClass(),
Optional.class);
private static final MethodAccessor GET_CHAT_TYPE = Accessors.getMethodAccessor(HANDLE_TYPE, "chatType");
private static final MethodAccessor GET_NAME = Accessors.getMethodAccessor(HANDLE_TYPE, "name");
private static final MethodAccessor GET_TARGET_NAME = Accessors.getMethodAccessor(HANDLE_TYPE, "targetName");
private static final EquivalentConverter<MinecraftKey> CHAT_TYPE_HOLDER_CONVERTER =
new EquivalentConverter<>() {
@Override
public Object getGeneric(MinecraftKey specific) {
return chatTypeHolderConverter().getGeneric(specific);
}
@Override
public MinecraftKey getSpecific(Object generic) {
return chatTypeHolderConverter().getSpecific(generic);
}
@Override
public Class<MinecraftKey> getSpecificType() {
return MinecraftKey.class;
}
};
public static final EquivalentConverter<WrappedChatTypeBound> CONVERTER =
Converters.ignoreNull(new EquivalentConverter<>() {
@Override
public Object getGeneric(WrappedChatTypeBound specific) {
return CONSTRUCTOR.invoke(
CHAT_TYPE_HOLDER_CONVERTER.getGeneric(specific.chatType),
BukkitConverters.getWrappedChatComponentConverter().getGeneric(specific.name),
Converters.optional(BukkitConverters.getWrappedChatComponentConverter())
.getGeneric(specific.targetName));
}
@Override
public WrappedChatTypeBound getSpecific(Object generic) {
return new WrappedChatTypeBound(
CHAT_TYPE_HOLDER_CONVERTER.getSpecific(GET_CHAT_TYPE.invoke(generic)),
BukkitConverters.getWrappedChatComponentConverter().getSpecific(GET_NAME.invoke(generic)),
Converters.optional(BukkitConverters.getWrappedChatComponentConverter())
.getSpecific(GET_TARGET_NAME.invoke(generic)));
}
@Override
public Class<WrappedChatTypeBound> getSpecificType() {
return WrappedChatTypeBound.class;
}
});
private final MinecraftKey chatType;
private final WrappedChatComponent name;
private final Optional<WrappedChatComponent> targetName;
public WrappedChatTypeBound(MinecraftKey chatType, WrappedChatComponent name,
Optional<WrappedChatComponent> targetName) {
this.chatType = chatType;
this.name = name;
this.targetName = targetName;
}
public MinecraftKey getChatType() {
return chatType;
}
public WrappedChatComponent getName() {
return name;
}
public Optional<WrappedChatComponent> getTargetName() {
return targetName;
}
private static EquivalentConverter<MinecraftKey> chatTypeHolderConverter() {
WrappedRegistry registry = WrappedRegistry.getRegistryByNmsKey("core.registries.Registries", "CHAT_TYPE");
if (registry == null) {
throw new IllegalStateException("ChatType registry is not available");
}
return Converters.holder(registry.valueConverter(), registry);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof WrappedChatTypeBound that)) {
return false;
}
return Objects.equals(chatType, that.chatType)
&& Objects.equals(name, that.name)
&& Objects.equals(targetName, that.targetName);
}
@Override
public int hashCode() {
return Objects.hash(chatType, name, targetName);
}
}
@@ -1,86 +0,0 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import java.util.Arrays;
import java.util.Objects;
/**
* Wrapper for {@code ClientboundChunksBiomesPacket.ChunkBiomeData}.
*/
public final class WrappedChunkBiomeData {
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getMinecraftClass(
"network.protocol.game.ClientboundChunksBiomesPacket$ChunkBiomeData");
private static final ConstructorAccessor CONSTRUCTOR = Accessors.getConstructorAccessor(
HANDLE_TYPE,
MinecraftReflection.getChunkCoordIntPair(),
byte[].class);
private static final MethodAccessor GET_POS = Accessors.getMethodAccessor(HANDLE_TYPE, "pos");
private static final MethodAccessor GET_BUFFER = Accessors.getMethodAccessor(HANDLE_TYPE, "buffer");
public static final EquivalentConverter<WrappedChunkBiomeData> CONVERTER =
Converters.ignoreNull(new EquivalentConverter<>() {
@Override
public Object getGeneric(WrappedChunkBiomeData specific) {
return CONSTRUCTOR.invoke(
ChunkCoordIntPair.getConverter().getGeneric(specific.position),
specific.buffer.clone());
}
@Override
public WrappedChunkBiomeData getSpecific(Object generic) {
return new WrappedChunkBiomeData(
ChunkCoordIntPair.getConverter().getSpecific(GET_POS.invoke(generic)),
((byte[]) GET_BUFFER.invoke(generic)).clone());
}
@Override
public Class<WrappedChunkBiomeData> getSpecificType() {
return WrappedChunkBiomeData.class;
}
});
private final ChunkCoordIntPair position;
private final byte[] buffer;
public WrappedChunkBiomeData(ChunkCoordIntPair position, byte[] buffer) {
this.position = position;
this.buffer = buffer.clone();
}
public ChunkCoordIntPair getPosition() {
return position;
}
public byte[] getBuffer() {
return buffer.clone();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof WrappedChunkBiomeData that)) {
return false;
}
return Objects.equals(position, that.position) && Arrays.equals(buffer, that.buffer);
}
@Override
public int hashCode() {
int result = Objects.hash(position);
result = 31 * result + Arrays.hashCode(buffer);
return result;
}
@Override
public String toString() {
return "WrappedChunkBiomeData{position=" + position + ", buffer=" + Arrays.toString(buffer) + '}';
}
}
@@ -0,0 +1,181 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.RecordComponent;
import java.util.Optional;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.cloning.AggregateCloner;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Represents a single data component in Minecraft, which is a key-value pair
* used to store metadata about game objects such as items, mobs, and block entities.
* <p>
* This class provides methods to create, access, and manipulate data components,
* as well as to convert them to and from their underlying NMS handles.
*
* @see WrappedDataComponentHolder
*/
public final class WrappedDataComponent {
private static final Optional<Class<?>> HANDLE_TYPE = MinecraftReflection.getTypedDataComponentClass();
private static MethodAccessor TYPE_ACCESSOR;
private static MethodAccessor VALUE_ACCESSOR;
private static ConstructorAccessor CTOR_ACCESSOR;
private final Object keyHandle;
private final String key;
private Object value;
static {
initialize();
}
private static void initialize() {
if (HANDLE_TYPE.isEmpty()) {
return;
}
Class<?> clazz = HANDLE_TYPE.get();
RecordComponent typeComponent = clazz.getRecordComponents()[0];
TYPE_ACCESSOR = Accessors.getMethodAccessor(typeComponent.getAccessor());
RecordComponent valueComponent = clazz.getRecordComponents()[1];
VALUE_ACCESSOR = Accessors.getMethodAccessor(valueComponent.getAccessor());
CTOR_ACCESSOR = Accessors.getConstructorAccessor(clazz, typeComponent.getType(), valueComponent.getType());
}
WrappedDataComponent(Object keyHandle, String key, Object value) {
this.keyHandle = keyHandle;
this.key = key;
this.value = value;
}
/**
* @return the key of this data component, e.g. "minecraft:custom_data".
*/
public String getKey() {
return key;
}
/**
* Updates the in-memory value of this data component.
* <br>
* <b>NOTE:</b> this does <b>NOT</b> update the underlying handle
* @param value new NMS value
*/
public void setValue(Object value) {
this.value = value;
}
/**
* @return the current in-memory value of this data component
* @param <T> desired return type
*/
@SuppressWarnings("unchecked")
public <T> T getValue() {
return (T) value;
}
/**
* Creates a new WrappedDataComponent from an existing NMS handle.
* @param handle NMS handle
* @return new WrappedDataComponent instance
*/
public static WrappedDataComponent fromHandle(Object handle) {
if (TYPE_ACCESSOR == null || VALUE_ACCESSOR == null) {
throw new IllegalStateException("Unable to access TypedDataComponent");
}
Object typeHandle = TYPE_ACCESSOR.invoke(handle);
Object value = VALUE_ACCESSOR.invoke(handle);
WrappedRegistry componentTypeRegistry = WrappedRegistry.getDataComponentTypeRegistry();
String key = componentTypeRegistry.getKey(typeHandle).getFullKey();
return new WrappedDataComponent(typeHandle, key, value);
}
/**
* Creates a new NMS handle for this data component using the current value.
* @return new NMS handle
*/
public Object getHandle() {
if (CTOR_ACCESSOR == null) {
throw new IllegalStateException("Unable to access TypedDataComponent constructor");
}
return CTOR_ACCESSOR.invoke(keyHandle, value);
}
/**
* Creates a new WrappedDataComponent with the specified key and no value.
* @param key the key of the data component, e.g. "minecraft:custom_data".
* @return new WrappedDataComponent instance
*/
public static WrappedDataComponent create(String key) {
return create(key, null);
}
/**
* Creates a new WrappedDataComponent with the specified key and value.
* @param key the key of the data component, e.g. "minecraft:custom_data".
* @param value the NMS value of the data component, e.g. an ItemStack or Integer.
* @return new WrappedDataComponent instance
*/
public static WrappedDataComponent create(String key, Object value) {
WrappedRegistry componentTypeRegistry = WrappedRegistry.getDataComponentTypeRegistry();
Object keyHandle = componentTypeRegistry.get(key);
if (keyHandle == null) {
throw new IllegalArgumentException("Invalid key: " + key);
}
return new WrappedDataComponent(keyHandle, key, value);
}
/**
* Creates a shallow clone of this WrappedDataComponent, which means it copies the key and value references,
* but does not clone the value itself. This should cover most cases where the value is immutable
* (e.g. strings, numbers, enums).
* @return a new WrappedDataComponent instance with the same key and value references
*/
public WrappedDataComponent shallowClone() {
return new WrappedDataComponent(keyHandle, key, value);
}
/**
* Creates a deep clone of this WrappedDataComponent, which means it clones the value as well.
* This is useful when the value is mutable (e.g. books, NBT data) and you want to ensure that
* changes to the cloned component do not affect the original component.
* @return a new WrappedDataComponent instance with a cloned value
*/
public WrappedDataComponent deepClone() {
Object clonedValue = AggregateCloner.DEFAULT.clone(value);
return new WrappedDataComponent(keyHandle, key, clonedValue);
}
@Override
public String toString() {
return "WrappedDataComponent[" + key + "=>" + value + "]";
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof WrappedDataComponent other)) return false;
return key.equals(other.key) && value.equals(other.value);
}
@Override
public int hashCode() {
int result = key.hashCode();
result = 31 * result + (value != null ? value.hashCode() : 0);
return result;
}
}
@@ -0,0 +1,77 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import org.bukkit.inventory.ItemStack;
/**
* Provides access to a DataComponentHolder, which is an object that can hold
* various (meta)data components
*/
public final class WrappedDataComponentHolder extends AbstractWrapper {
private static MethodAccessor GET_COMPONENTS;
private static MethodAccessor GET_COMPONENT;
private static Optional<Class<?>> DATA_COMPONENT_MAP_CLASS = MinecraftReflection.getDataComponentMapClass();
private static Optional<Class<?>> DATA_COMPONENT_HOLDER_CLASS = MinecraftReflection.getDataComponentHolderClass();
static {
initialize();
}
private static void initialize() {
if (DATA_COMPONENT_MAP_CLASS.isEmpty() || DATA_COMPONENT_HOLDER_CLASS.isEmpty()) {
return;
}
Class<?> mapClass = DATA_COMPONENT_MAP_CLASS.get();
Class<?> holderClass = DATA_COMPONENT_HOLDER_CLASS.get();
FuzzyReflection fuzzy = FuzzyReflection.fromClass(holderClass, true);
GET_COMPONENTS = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterCount(0)
.returnTypeExact(mapClass)
.build()));
GET_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterCount(1)
//.requireModifier(Modifier.PUBLIC)
.banModifier(Modifier.STATIC)
.parameterExactType(MinecraftReflection.getMinecraftClass("core.component.DataComponentType"))
.build()));
}
public WrappedDataComponentHolder(Object handle) {
super(DATA_COMPONENT_HOLDER_CLASS.orElseThrow(() -> new IllegalStateException("DataComponentHolder class is not available in this version")));
setHandle(handle);
}
public Map<String, WrappedDataComponent> getAllComponents() {
Iterable<?> components = (Iterable<?>) GET_COMPONENTS.invoke(handle);
Map<String, WrappedDataComponent> wrapped = new HashMap<>();
for (Object componentHandle : components) {
WrappedDataComponent component = WrappedDataComponent.fromHandle(componentHandle);
wrapped.put(component.getKey(), component);
}
return wrapped;
}
public WrappedDataComponent getComponent(String key) {
Object keyHandle = WrappedRegistry.getDataComponentTypeRegistry().get(key);
Object valueHandle = GET_COMPONENT.invoke(handle, keyHandle);
return new WrappedDataComponent(keyHandle, key, valueHandle);
}
public static WrappedDataComponentHolder fromItemStack(ItemStack itemStack) {
Object nmsStack = MinecraftReflection.getMinecraftItemStack(itemStack);
return new WrappedDataComponentHolder(nmsStack);
}
}
@@ -36,12 +36,12 @@ import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.utility.Validate;
import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
import com.comphenix.protocol.wrappers.EnumWrappers.EntityPose;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.google.common.collect.ImmutableBiMap;
import org.apache.commons.lang.Validate;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
@@ -1,140 +0,0 @@
package com.comphenix.protocol.wrappers;
import java.util.Objects;
import com.comphenix.protocol.events.InternalStructure;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Wraps the NMS {@code ExplosionParticleInfo} record, which pairs a
* {@link WrappedParticle} with scaling and speed float values.
*
* <p>NMS record: {@code ExplosionParticleInfo(ParticleOptions particle, float scaling, float speed)}
*/
public class WrappedExplosionParticleInfo {
private static final Class<?> NMS_CLASS;
private static final ConstructorAccessor NMS_CTOR;
static {
NMS_CLASS = MinecraftReflection.getMinecraftClass("core.particles.ExplosionParticleInfo");
NMS_CTOR = Accessors.getConstructorAccessor(
NMS_CLASS,
MinecraftReflection.getParticleParam(),
float.class,
float.class);
}
/**
* Returns the NMS {@code ExplosionParticleInfo} class.
*/
public static Class<?> getNmsClass() {
return NMS_CLASS;
}
// ── Instance data ────────────────────────────────────────────────────
private WrappedParticle<?> particle;
private float scaling;
private float speed;
public WrappedExplosionParticleInfo() {
this.scaling = 1.0f;
this.speed = 1.0f;
}
public WrappedExplosionParticleInfo(WrappedParticle<?> particle, float scaling, float speed) {
this.particle = particle;
this.scaling = scaling;
this.speed = speed;
}
public WrappedParticle<?> getParticle() {
return particle;
}
public void setParticle(WrappedParticle<?> particle) {
this.particle = particle;
}
public float getScaling() {
return scaling;
}
public void setScaling(float scaling) {
this.scaling = scaling;
}
public float getSpeed() {
return speed;
}
public void setSpeed(float speed) {
this.speed = speed;
}
// ── Converter ────────────────────────────────────────────────────────
/**
* Reads an NMS {@code ExplosionParticleInfo} into this wrapper via
* {@link InternalStructure} typed getters.
*/
public static WrappedExplosionParticleInfo fromHandle(InternalStructure handle) {
WrappedParticle<?> particle = handle.getNewParticles().read(0);
float scaling = handle.getFloat().read(0);
float speed = handle.getFloat().read(1);
return new WrappedExplosionParticleInfo(particle, scaling, speed);
}
/**
* Returns an {@link EquivalentConverter} between {@code WrappedExplosionParticleInfo}
* and the NMS {@code ExplosionParticleInfo} record.
*/
public static EquivalentConverter<WrappedExplosionParticleInfo> getConverter() {
return new EquivalentConverter<>() {
@Override
public WrappedExplosionParticleInfo getSpecific(Object generic) {
InternalStructure structure = InternalStructure.getConverter().getSpecific(generic);
return fromHandle(structure);
}
@Override
public Object getGeneric(WrappedExplosionParticleInfo specific) {
EquivalentConverter<WrappedParticle> particleConverter = BukkitConverters.getParticleConverter();
Object nmsParticle = particleConverter.getGeneric(specific.particle);
return NMS_CTOR.invoke(nmsParticle, specific.scaling, specific.speed);
}
@Override
public Class<WrappedExplosionParticleInfo> getSpecificType() {
return WrappedExplosionParticleInfo.class;
}
};
}
// ── Object methods ───────────────────────────────────────────────────
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof WrappedExplosionParticleInfo that)) return false;
return Float.compare(scaling, that.scaling) == 0
&& Float.compare(speed, that.speed) == 0
&& Objects.equals(particle, that.particle);
}
@Override
public int hashCode() {
return Objects.hash(particle, scaling, speed);
}
@Override
public String toString() {
return "WrappedExplosionParticleInfo{particle=" + particle
+ ", scaling=" + scaling + ", speed=" + speed + "}";
}
}
@@ -35,30 +35,18 @@ public class WrappedGameProfile extends AbstractWrapper {
GAME_PROFILE, String.class, String.class);
private static final ConstructorAccessor CREATE_UUID_STRING = Accessors.getConstructorAccessorOrNull(
GAME_PROFILE, UUID.class, String.class);
private static final ConstructorAccessor CREATE_UUID_STRING_PROPERTIES = Accessors.getConstructorAccessorOrNull(
GAME_PROFILE, UUID.class, String.class, MinecraftReflection.getGameProfilePropertyMapClass());
private static final FieldAccessor GET_UUID_STRING = Accessors.getFieldAccessorOrNull(
GAME_PROFILE, "id", String.class);
private static MethodAccessor GET_ID;
private static MethodAccessor GET_NAME;
private static MethodAccessor GET_PROPERTIES;
private static MethodAccessor IS_COMPLETE;
static {
GET_ID = Accessors.getMethodAccessorOrNull(GAME_PROFILE, "getId");
if (GET_ID == null) {
GET_ID = Accessors.getMethodAccessorOrNull(GAME_PROFILE, "id");
GET_NAME = Accessors.getMethodAccessorOrNull(GAME_PROFILE, "name");
GET_PROPERTIES = Accessors.getMethodAccessorOrNull(GAME_PROFILE, "properties");
IS_COMPLETE = Accessors.getMethodAccessorOrNull(GAME_PROFILE, "complete");
} else {
GET_NAME = Accessors.getMethodAccessorOrNull(GAME_PROFILE, "getName");
GET_PROPERTIES = Accessors.getMethodAccessorOrNull(GAME_PROFILE, "getProperties");
IS_COMPLETE = Accessors.getMethodAccessorOrNull(GAME_PROFILE, "isComplete");
}
}
private static final MethodAccessor GET_ID = Accessors.getMethodAccessorOrNull(
GAME_PROFILE, "getId");
private static final MethodAccessor GET_NAME = Accessors.getMethodAccessorOrNull(
GAME_PROFILE, "getName");
private static final MethodAccessor GET_PROPERTIES = Accessors.getMethodAccessorOrNull(
GAME_PROFILE, "getProperties");
private static final MethodAccessor IS_COMPLETE = Accessors.getMethodAccessorOrNull(
GAME_PROFILE, "isComplete");
// Fetching game profile
private static FieldAccessor PLAYER_PROFILE;
@@ -129,51 +117,6 @@ public class WrappedGameProfile extends AbstractWrapper {
this(parseUUID(id), name);
}
private static Object createHandle(UUID uuid, String name, Multimap<String, WrappedSignedProperty> properties) {
if (CREATE_STRING_STRING != null) {
return CREATE_STRING_STRING.invoke(uuid != null ? uuid.toString() : null, name);
}
if (CREATE_UUID_STRING == null) {
throw new IllegalArgumentException("Unsupported GameProfile constructor.");
}
if (!MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
return CREATE_UUID_STRING.invoke(uuid, name);
}
// 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");
}
// 1.21.9+ made PropertyMap's underlying map immutable, so we need to override it with a mutable map
if (MinecraftVersion.v1_21_9.atOrAbove()) {
return CREATE_UUID_STRING_PROPERTIES.invoke(uuid == null ? MinecraftGenerator.SYS_UUID : uuid, name == null ? "" : name,
convertPropertyMap(properties));
}
return CREATE_UUID_STRING.invoke(uuid == null ? MinecraftGenerator.SYS_UUID : uuid, name == null ? "" : name);
}
private static Object convertPropertyMap(Multimap<String, WrappedSignedProperty> properties) {
com.comphenix.protocol.wrappers.MutablePropertyMap map =
new com.comphenix.protocol.wrappers.MutablePropertyMap();
if (properties == null || properties.isEmpty()) {
return map;
}
for (String key : properties.keySet()) {
for (WrappedSignedProperty property : properties.get(key)) {
map.put(key, (com.mojang.authlib.properties.Property) property.getHandle());
}
}
return map;
}
/**
* Construct a new game profile with the given properties.
* <p>
@@ -184,12 +127,24 @@ public class WrappedGameProfile extends AbstractWrapper {
*/
public WrappedGameProfile(UUID uuid, String name) {
super(GAME_PROFILE);
setHandle(createHandle(uuid, name, null));
}
public WrappedGameProfile(UUID uuid, String name, Multimap<String, WrappedSignedProperty> properties) {
super(GAME_PROFILE);
setHandle(createHandle(uuid, name, properties));
if (CREATE_STRING_STRING != null) {
setHandle(CREATE_STRING_STRING.invoke(uuid != null ? uuid.toString() : null, name));
} else if (CREATE_UUID_STRING != null) {
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.");
}
}
/**
@@ -202,12 +157,7 @@ public class WrappedGameProfile extends AbstractWrapper {
if (handle == null)
return null;
WrappedGameProfile delegate = new WrappedGameProfile(handle);
if (MinecraftVersion.v1_21_9.atOrAbove()) {
return new WrappedGameProfile(delegate.getUUID(), delegate.getName(), delegate.getProperties());
} else {
return delegate;
}
return new WrappedGameProfile(handle);
}
/**
@@ -319,15 +269,7 @@ public class WrappedGameProfile extends AbstractWrapper {
if (result == null) {
Multimap properties = (Multimap) GET_PROPERTIES.invoke(handle);
Multimap inner;
if (MinecraftVersion.v1_21_9.atOrAbove()) {
inner = properties;
} else {
inner = GuavaWrappers.getBukkitMultimap(properties);
}
result = new ConvertedMultimap<String, Object, WrappedSignedProperty>(inner) {
result = new ConvertedMultimap<String, Object, WrappedSignedProperty>(GuavaWrappers.getBukkitMultimap(properties)) {
@Override
protected Object toInner(WrappedSignedProperty outer) {
return outer.handle;
@@ -1,95 +0,0 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import java.util.Objects;
/**
* Wrapper for {@code net.minecraft.server.packs.repository.KnownPack}.
*/
public final class WrappedKnownPack {
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getMinecraftClass("server.packs.repository.KnownPack");
private static final ConstructorAccessor CONSTRUCTOR = Accessors.getConstructorAccessor(
HANDLE_TYPE,
String.class,
String.class,
String.class);
private static final MethodAccessor GET_NAMESPACE = Accessors.getMethodAccessor(HANDLE_TYPE, "namespace");
private static final MethodAccessor GET_ID = Accessors.getMethodAccessor(HANDLE_TYPE, "id");
private static final MethodAccessor GET_VERSION = Accessors.getMethodAccessor(HANDLE_TYPE, "version");
public static final EquivalentConverter<WrappedKnownPack> CONVERTER =
Converters.ignoreNull(new EquivalentConverter<>() {
@Override
public Object getGeneric(WrappedKnownPack specific) {
return CONSTRUCTOR.invoke(specific.namespace, specific.id, specific.version);
}
@Override
public WrappedKnownPack getSpecific(Object generic) {
return new WrappedKnownPack(
(String) GET_NAMESPACE.invoke(generic),
(String) GET_ID.invoke(generic),
(String) GET_VERSION.invoke(generic));
}
@Override
public Class<WrappedKnownPack> getSpecificType() {
return WrappedKnownPack.class;
}
});
private final String namespace;
private final String id;
private final String version;
public WrappedKnownPack(String namespace, String id, String version) {
this.namespace = namespace;
this.id = id;
this.version = version;
}
public String getNamespace() {
return namespace;
}
public String getId() {
return id;
}
public String getVersion() {
return version;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof WrappedKnownPack that)) {
return false;
}
return Objects.equals(namespace, that.namespace)
&& Objects.equals(id, that.id)
&& Objects.equals(version, that.version);
}
@Override
public int hashCode() {
return Objects.hash(namespace, id, version);
}
@Override
public String toString() {
return "WrappedKnownPack{"
+ "namespace='" + namespace + '\''
+ ", id='" + id + '\''
+ ", version='" + version + '\''
+ '}';
}
}
@@ -1,96 +0,0 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import java.util.BitSet;
import java.util.Objects;
/**
* Wrapper for {@code LastSeenMessages.Update}.
*/
public final class WrappedLastSeenMessagesUpdate {
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getMinecraftClass("network.chat.LastSeenMessages$Update");
private static final ConstructorAccessor CONSTRUCTOR = Accessors.getConstructorAccessor(
HANDLE_TYPE,
int.class,
BitSet.class,
byte.class);
private static final MethodAccessor GET_OFFSET = Accessors.getMethodAccessor(HANDLE_TYPE, "offset");
private static final MethodAccessor GET_ACKNOWLEDGED = Accessors.getMethodAccessor(HANDLE_TYPE, "acknowledged");
private static final MethodAccessor GET_CHECKSUM = Accessors.getMethodAccessor(HANDLE_TYPE, "checksum");
public static final EquivalentConverter<WrappedLastSeenMessagesUpdate> CONVERTER =
Converters.ignoreNull(new EquivalentConverter<>() {
@Override
public Object getGeneric(WrappedLastSeenMessagesUpdate specific) {
return CONSTRUCTOR.invoke(specific.offset, specific.getAcknowledged(), specific.checksum);
}
@Override
public WrappedLastSeenMessagesUpdate getSpecific(Object generic) {
return new WrappedLastSeenMessagesUpdate(
(int) GET_OFFSET.invoke(generic),
(BitSet) GET_ACKNOWLEDGED.invoke(generic),
(byte) GET_CHECKSUM.invoke(generic));
}
@Override
public Class<WrappedLastSeenMessagesUpdate> getSpecificType() {
return WrappedLastSeenMessagesUpdate.class;
}
});
private final int offset;
private final BitSet acknowledged;
private final byte checksum;
public WrappedLastSeenMessagesUpdate(int offset, BitSet acknowledged, byte checksum) {
this.offset = offset;
this.acknowledged = (BitSet) acknowledged.clone();
this.checksum = checksum;
}
public int getOffset() {
return offset;
}
public BitSet getAcknowledged() {
return (BitSet) acknowledged.clone();
}
public byte getChecksum() {
return checksum;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof WrappedLastSeenMessagesUpdate that)) {
return false;
}
return offset == that.offset
&& checksum == that.checksum
&& Objects.equals(acknowledged, that.acknowledged);
}
@Override
public int hashCode() {
return Objects.hash(offset, acknowledged, checksum);
}
@Override
public String toString() {
return "WrappedLastSeenMessagesUpdate{"
+ "offset=" + offset
+ ", acknowledged=" + acknowledged
+ ", checksum=" + checksum
+ '}';
}
}
@@ -1,126 +0,0 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import org.bukkit.util.Vector;
import java.util.Objects;
/**
* Wrapper for {@code NewMinecartBehavior.MinecartStep}.
*/
public final class WrappedMinecartStep {
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getMinecraftClass(
"world.entity.vehicle.minecart.NewMinecartBehavior$MinecartStep");
private static final ConstructorAccessor CONSTRUCTOR = Accessors.getConstructorAccessor(
HANDLE_TYPE,
MinecraftReflection.getVec3DClass(),
MinecraftReflection.getVec3DClass(),
float.class,
float.class,
float.class);
private static final MethodAccessor GET_POSITION = Accessors.getMethodAccessor(HANDLE_TYPE, "position");
private static final MethodAccessor GET_MOVEMENT = Accessors.getMethodAccessor(HANDLE_TYPE, "movement");
private static final MethodAccessor GET_Y_ROT = Accessors.getMethodAccessor(HANDLE_TYPE, "yRot");
private static final MethodAccessor GET_X_ROT = Accessors.getMethodAccessor(HANDLE_TYPE, "xRot");
private static final MethodAccessor GET_WEIGHT = Accessors.getMethodAccessor(HANDLE_TYPE, "weight");
public static final EquivalentConverter<WrappedMinecartStep> CONVERTER =
Converters.ignoreNull(new EquivalentConverter<>() {
@Override
public Object getGeneric(WrappedMinecartStep specific) {
EquivalentConverter<Vector> vectorConverter = BukkitConverters.getVectorConverter();
return CONSTRUCTOR.invoke(
vectorConverter.getGeneric(specific.position),
vectorConverter.getGeneric(specific.movement),
specific.yRot,
specific.xRot,
specific.weight);
}
@Override
public WrappedMinecartStep getSpecific(Object generic) {
EquivalentConverter<Vector> vectorConverter = BukkitConverters.getVectorConverter();
return new WrappedMinecartStep(
vectorConverter.getSpecific(GET_POSITION.invoke(generic)),
vectorConverter.getSpecific(GET_MOVEMENT.invoke(generic)),
(float) GET_Y_ROT.invoke(generic),
(float) GET_X_ROT.invoke(generic),
(float) GET_WEIGHT.invoke(generic));
}
@Override
public Class<WrappedMinecartStep> getSpecificType() {
return WrappedMinecartStep.class;
}
});
private final Vector position;
private final Vector movement;
private final float yRot;
private final float xRot;
private final float weight;
public WrappedMinecartStep(Vector position, Vector movement, float yRot, float xRot, float weight) {
this.position = position.clone();
this.movement = movement.clone();
this.yRot = yRot;
this.xRot = xRot;
this.weight = weight;
}
public Vector getPosition() {
return position.clone();
}
public Vector getMovement() {
return movement.clone();
}
public float getYRot() {
return yRot;
}
public float getXRot() {
return xRot;
}
public float getWeight() {
return weight;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof WrappedMinecartStep that)) {
return false;
}
return Float.compare(yRot, that.yRot) == 0
&& Float.compare(xRot, that.xRot) == 0
&& Float.compare(weight, that.weight) == 0
&& Objects.equals(position, that.position)
&& Objects.equals(movement, that.movement);
}
@Override
public int hashCode() {
return Objects.hash(position, movement, yRot, xRot, weight);
}
@Override
public String toString() {
return "WrappedMinecartStep{"
+ "position=" + position
+ ", movement=" + movement
+ ", yRot=" + yRot
+ ", xRot=" + xRot
+ ", weight=" + weight
+ '}';
}
}
@@ -1,183 +0,0 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import org.bukkit.util.Vector;
/**
* Wrapper around the NMS {@code PositionMoveRotation} record, introduced in Minecraft 1.21.2.
* <p>
* This type is used in packets such as {@code ENTITY_TELEPORT} and {@code ENTITY_POSITION_SYNC}
* to represent combined position, movement delta and rotation.
* <p>
* Example usage:
* <pre>{@code
* WrappedPositionMoveRotation posRot = WrappedPositionMoveRotation.create(
* new Vector(x, y, z), // position
* new Vector(0, 0, 0), // deltaMovement
* yaw, // yRot
* pitch // xRot
* );
* packet.getPositionMoveRotation().write(0, posRot);
* }</pre>
*/
public class WrappedPositionMoveRotation extends AbstractWrapper {
private static Class<?> NMS_CLASS;
private static ConstructorAccessor CONSTRUCTOR;
private static StructureModifier<Object> BASE_MODIFIER;
private final StructureModifier<Object> modifier;
private WrappedPositionMoveRotation(Object handle) {
super(getNmsClass());
setHandle(handle);
this.modifier = getBaseModifier().withTarget(handle);
}
private static Class<?> getNmsClass() {
if (NMS_CLASS == null) {
NMS_CLASS = MinecraftReflection.getPositionMoveRotationClass();
}
return NMS_CLASS;
}
private static StructureModifier<Object> getBaseModifier() {
if (BASE_MODIFIER == null) {
BASE_MODIFIER = new StructureModifier<>(getNmsClass());
}
return BASE_MODIFIER;
}
private static ConstructorAccessor getConstructor() {
if (CONSTRUCTOR == null) {
CONSTRUCTOR = Accessors.getConstructorAccessor(
getNmsClass(),
MinecraftReflection.getVec3DClass(),
MinecraftReflection.getVec3DClass(),
float.class,
float.class
);
}
return CONSTRUCTOR;
}
/**
* Creates a new {@code WrappedPositionMoveRotation} from the given NMS handle.
*
* @param handle - the NMS {@code PositionMoveRotation} instance.
* @return the wrapper.
*/
public static WrappedPositionMoveRotation fromHandle(Object handle) {
return new WrappedPositionMoveRotation(handle);
}
/**
* Creates a new {@code WrappedPositionMoveRotation} with the given values.
*
* @param position - the absolute position.
* @param deltaMovement - the movement delta (velocity).
* @param yRot - the yaw rotation.
* @param xRot - the pitch rotation.
* @return a new wrapper instance.
*/
public static WrappedPositionMoveRotation create(Vector position, Vector deltaMovement, float yRot, float xRot) {
EquivalentConverter<Vector> conv = BukkitConverters.getVectorConverter();
Object handle = getConstructor().invoke(
conv.getGeneric(position),
conv.getGeneric(deltaMovement),
yRot,
xRot
);
return new WrappedPositionMoveRotation(handle);
}
/**
* Retrieves the absolute position component.
*
* @return the position as a Bukkit {@link Vector}.
*/
public Vector getPosition() {
return modifier.withType(MinecraftReflection.getVec3DClass(), BukkitConverters.getVectorConverter()).read(0);
}
/**
* Sets the absolute position component.
*
* @param position - the new position.
*/
public void setPosition(Vector position) {
modifier.withType(MinecraftReflection.getVec3DClass(), BukkitConverters.getVectorConverter()).write(0, position);
}
/**
* Retrieves the movement delta (velocity) component.
*
* @return the delta movement as a Bukkit {@link Vector}.
*/
public Vector getDeltaMovement() {
return modifier.withType(MinecraftReflection.getVec3DClass(), BukkitConverters.getVectorConverter()).read(1);
}
/**
* Sets the movement delta (velocity) component.
*
* @param deltaMovement - the new delta movement.
*/
public void setDeltaMovement(Vector deltaMovement) {
modifier.withType(MinecraftReflection.getVec3DClass(), BukkitConverters.getVectorConverter()).write(1, deltaMovement);
}
/**
* Retrieves the yaw rotation (y-axis rotation).
*
* @return the yaw as a float.
*/
public float getYRot() {
StructureModifier<Float> floats = modifier.withType(float.class);
return floats.read(0);
}
/**
* Sets the yaw rotation (y-axis rotation).
*
* @param yRot - the new yaw value.
*/
public void setYRot(float yRot) {
modifier.withType(float.class).write(0, yRot);
}
/**
* Retrieves the pitch rotation (x-axis rotation).
*
* @return the pitch as a float.
*/
public float getXRot() {
StructureModifier<Float> floats = modifier.withType(float.class);
return floats.read(1);
}
/**
* Sets the pitch rotation (x-axis rotation).
*
* @param xRot - the new pitch value.
*/
public void setXRot(float xRot) {
modifier.withType(float.class).write(1, xRot);
}
/**
* Returns an {@link EquivalentConverter} that converts between {@code WrappedPositionMoveRotation}
* and the underlying NMS handle.
*
* @return the converter.
*/
public static EquivalentConverter<WrappedPositionMoveRotation> getConverter() {
return Converters.handle(AbstractWrapper::getHandle,
WrappedPositionMoveRotation::fromHandle, WrappedPositionMoveRotation.class);
}
}
@@ -1,59 +1,88 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftRegistryAccess;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.collect.ImmutableMap;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMatchers;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.collect.ImmutableMap;
public class WrappedRegistry {
// map of NMS class to registry instance (static/built-in registries)
private static final Map<Class<?>, WrappedRegistry> REGISTRY;
// map of NMS class to registry instance
private static final Map<Class<?>, WrappedRegistry> BY_CLASS;
private static final Map<String, WrappedRegistry> BY_KEY;
// map of NMS class to ResourceKey for dynamic (datapack) registries
private static final Map<Class<?>, Object> DYNAMIC_REGISTRY_KEYS;
// ResourceKeys to try when generic metadata is unavailable or incomplete
private static final Set<Object> DYNAMIC_REGISTRY_FALLBACK_KEYS;
// cache of dynamically resolved registries
private static final Map<Class<?>, WrappedRegistry> DYNAMIC_REGISTRY_CACHE = new ConcurrentHashMap<>();
private static final Map<Object, WrappedRegistry> DYNAMIC_REGISTRY_KEY_CACHE = new ConcurrentHashMap<>();
// RegistryAccess.lookup(ResourceKey) -> Optional<Registry>
private static final MethodAccessor REGISTRY_ACCESS_LOOKUP;
private static final MethodAccessor GET;
private static final MethodAccessor GET_ID;
private static final MethodAccessor GET_KEY;
private static final MethodAccessor GET_BY_KEY;
private static final MethodAccessor GET_ITEM_ID;
private static final MethodAccessor GET_ITEM_KEY;
private static final MethodAccessor GET_REGISTRY_KEY;
private static final MethodAccessor GET_HOLDER;
static {
Map<Class<?>, WrappedRegistry> regMap = new HashMap<>();
Class<?> iRegistry = MinecraftReflection.getIRegistry();
FuzzyReflection fuzzy = FuzzyReflection.fromClass(iRegistry, false);
GET_BY_KEY = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(1)
.returnTypeMatches(FuzzyMatchers.and(FuzzyMatchers.assignable(Object.class), FuzzyMatchers.except(Optional.class)))
.requireModifier(Modifier.ABSTRACT)
.parameterExactType(MinecraftReflection.getMinecraftKeyClass())
.build()));
GET_ITEM_ID = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(1)
.returnTypeExact(int.class)
.requireModifier(Modifier.ABSTRACT)
.parameterDerivedOf(Object.class)
.build()));
GET_ITEM_KEY = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(1)
.returnTypeExact(MinecraftReflection.getMinecraftKeyClass())
.build()));
GET_REGISTRY_KEY = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(0)
.returnTypeExact(MinecraftReflection.getResourceKey())
.build()));
MethodAccessor getHolder = null;
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
try {
getHolder = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(1)
.banModifier(Modifier.STATIC)
.returnTypeExact(MinecraftReflection.getHolderClass())
.requireModifier(Modifier.PUBLIC)
.build()));
} catch (IllegalArgumentException ignored) {
}
}
GET_HOLDER = getHolder;
Map<Class<?>, WrappedRegistry> byClass = new HashMap<>();
Map<String, WrappedRegistry> byKey = new HashMap<>();
// get the possible registry fields
Class<?> iRegistry = MinecraftReflection.getIRegistry();
Class<?> builtInRegistries = MinecraftReflection.getBuiltInRegistries();
Set<Field> registries = FuzzyReflection.combineArrays(
iRegistry == null ? null : iRegistry.getFields(),
@@ -63,187 +92,75 @@ public class WrappedRegistry {
for (Field field : registries) {
try {
// make sure it's actually a registry
if (iRegistry.isAssignableFrom(field.getType())) {
Type genType = field.getGenericType();
if (genType instanceof ParameterizedType) {
ParameterizedType par = (ParameterizedType) genType;
Type paramType = par.getActualTypeArguments()[0];
if (paramType instanceof Class) {
// for example Registry<Item>
regMap.put((Class<?>) paramType, new WrappedRegistry(field.get(null)));
} else if (paramType instanceof ParameterizedType) {
// for example Registry<EntityType<?>>
par = (ParameterizedType) paramType;
paramType = par.getActualTypeArguments()[0];
if (paramType instanceof WildcardType) {
// some registry types are even more nested, but we don't want them
// for example Registry<Codec<ChunkGenerator>>, Registry<Codec<WorldChunkManager>>
WildcardType wildcard = (WildcardType) paramType;
if (wildcard.getUpperBounds().length != 1 || wildcard.getLowerBounds().length > 0) {
continue;
}
// we only want types with an undefined upper bound (aka. ?)
if (wildcard.getUpperBounds()[0] != Object.class) {
continue;
}
}
paramType = par.getRawType();
if (paramType instanceof Class<?>) {
// there might be duplicate registries, like the codec registries
// we don't want them to be registered here
regMap.put((Class<?>) paramType, new WrappedRegistry(field.get(null)));
}
}
}
}
} catch (ReflectiveOperationException ex) {
ProtocolLogger.debug("Failed to inspect built-in registry field " + field, ex);
}
}
}
REGISTRY = ImmutableMap.copyOf(regMap);
// Build dynamic registry key map from Registries class (ResourceKey<Registry<T>> fields)
// These cover datapack registries like DamageType that are not in BuiltInRegistries
Map<Class<?>, Object> dynKeyMap = new HashMap<>();
// LinkedHashSet so the generics-stripped fallback iterates in declaration order
// on Registries, giving deterministic resolution across JVM runs.
Set<Object> dynKeyFallbacks = new LinkedHashSet<>();
try {
Class<?> registriesClass = MinecraftReflection.getNullableNMS("core.registries.Registries");
Class<?> resourceKeyClass = MinecraftReflection.getResourceKey();
if (registriesClass != null) {
for (Field field : registriesClass.getFields()) {
if (!Modifier.isStatic(field.getModifiers())) continue;
if (!resourceKeyClass.isAssignableFrom(field.getType())) continue;
Object registryKey;
try {
registryKey = field.get(null);
} catch (ReflectiveOperationException ex) {
ProtocolLogger.debug("Failed to read dynamic registry key field " + field, ex);
if (!iRegistry.isAssignableFrom(field.getType())) {
continue;
}
dynKeyFallbacks.add(registryKey);
// looking for ResourceKey<Registry<T>> fields
Class<?> entryClass = getRegistryEntryClass(field, iRegistry);
if (entryClass != null && !regMap.containsKey(entryClass)) {
dynKeyMap.put(entryClass, registryKey);
Type genType = field.getGenericType();
if (!(genType instanceof ParameterizedType par)) {
continue;
}
Class<?> registryClass = null;
Type paramType = par.getActualTypeArguments()[0];
if (paramType instanceof Class<?> paramClass) {
// for example Registry<Item>
registryClass = paramClass;
} else if (paramType instanceof ParameterizedType) {
// for example Registry<EntityType<?>>
par = (ParameterizedType) paramType;
paramType = par.getActualTypeArguments()[0];
if (paramType instanceof WildcardType wildcard) {
// some registry types are even more nested, but we don't want them
// for example Registry<Codec<ChunkGenerator>>, Registry<Codec<WorldChunkManager>>
if (wildcard.getUpperBounds().length != 1 || wildcard.getLowerBounds().length > 0) {
continue;
}
// we only want types with an undefined upper bound (aka. ?)
if (wildcard.getUpperBounds()[0] != Object.class) {
continue;
}
}
paramType = par.getRawType();
if (paramType instanceof Class<?> paramClass) {
// there might be duplicate registries, like the codec registries
// we don't want them to be registered here
registryClass = paramClass;
}
}
Object handle = field.get(null);
ResourceKey key = ResourceKey.fromGeneric(GET_REGISTRY_KEY.invoke(handle));
WrappedRegistry wrapped = new WrappedRegistry(handle, key);
if (registryClass != null) {
byClass.put(registryClass, wrapped);
}
String registryName = key.getLocation().getFullKey();
byKey.put(registryName, wrapped);
} catch (ReflectiveOperationException ignored) {
}
}
} catch (Exception ex) {
ProtocolLogger.debug("Failed to inspect dynamic registry keys", ex);
}
DYNAMIC_REGISTRY_KEYS = ImmutableMap.copyOf(dynKeyMap);
DYNAMIC_REGISTRY_FALLBACK_KEYS = Collections.unmodifiableSet(dynKeyFallbacks);
// Set up RegistryAccess.lookup(ResourceKey) -> Optional<Registry>
MethodAccessor registryAccessLookup = null;
try {
Class<?> registryAccessClass = MinecraftReflection.getRegistryAccessClass();
if (registryAccessClass != null) {
registryAccessLookup = Accessors.getMethodAccessor(
FuzzyReflection.fromClass(registryAccessClass, false).getMethod(
FuzzyMethodContract.newBuilder()
.parameterExactArray(MinecraftReflection.getResourceKey())
.returnTypeExact(Optional.class)
.build()
)
);
}
} catch (Exception ex) {
ProtocolLogger.debug("Failed to find RegistryAccess.lookup(ResourceKey)", ex);
}
REGISTRY_ACCESS_LOOKUP = registryAccessLookup;
FuzzyReflection fuzzy = FuzzyReflection.fromClass(iRegistry, false);
GET = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(1)
.returnTypeMatches(FuzzyMatchers.and(FuzzyMatchers.assignable(Object.class), FuzzyMatchers.except(Optional.class)))
.requireModifier(Modifier.ABSTRACT)
.parameterExactType(MinecraftReflection.getMinecraftKeyClass())
.build()));
GET_ID = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(1)
.returnTypeExact(int.class)
.requireModifier(Modifier.ABSTRACT)
.parameterDerivedOf(Object.class)
.build()));
GET_KEY = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(1)
.returnTypeExact(MinecraftReflection.getMinecraftKeyClass())
.build()));
MethodAccessor getHolder = null;
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
try {
getHolder = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract
.newBuilder()
.parameterCount(1)
.banModifier(Modifier.STATIC)
.returnTypeExact(MinecraftReflection.getHolderClass())
.requireModifier(Modifier.PUBLIC)
.build()));
} catch (IllegalArgumentException ex) {
ProtocolLogger.debug("Failed to find Registry.getHolder method", ex);
}
}
GET_HOLDER = getHolder;
}
@Nullable
private static Class<?> getRegistryEntryClass(Field field, Class<?> iRegistry) {
Type genType = field.getGenericType();
if (!(genType instanceof ParameterizedType)) return null;
ParameterizedType outer = (ParameterizedType) genType;
if (outer.getActualTypeArguments().length != 1) return null;
Type innerType = outer.getActualTypeArguments()[0];
if (!(innerType instanceof ParameterizedType)) return null;
ParameterizedType registryType = (ParameterizedType) innerType;
if (!(registryType.getRawType() instanceof Class<?>)) return null;
Class<?> rawRegistry = (Class<?>) registryType.getRawType();
if (iRegistry != null && !iRegistry.isAssignableFrom(rawRegistry)) return null;
if (registryType.getActualTypeArguments().length != 1) return null;
Type entryType = registryType.getActualTypeArguments()[0];
if (entryType instanceof Class<?>) {
return (Class<?>) entryType;
} else if (entryType instanceof ParameterizedType) {
Type raw = ((ParameterizedType) entryType).getRawType();
if (raw instanceof Class<?>) return (Class<?>) raw;
}
return null;
BY_CLASS = ImmutableMap.copyOf(byClass);
BY_KEY = ImmutableMap.copyOf(byKey);
}
private final Object handle;
private final ResourceKey key;
/** The NMS {@code ResourceKey<Registry<T>>} for this registry, if known. */
@Nullable
private final Object registryKey;
private WrappedRegistry(Object handle) {
this(handle, null);
}
private WrappedRegistry(Object handle, @Nullable Object registryKey) {
private WrappedRegistry(Object handle, ResourceKey key) {
this.handle = handle;
this.registryKey = registryKey;
this.key = key;
}
public Object get(MinecraftKey key) {
return GET.invoke(handle, MinecraftKey.getConverter().getGeneric(key));
return GET_BY_KEY.invoke(handle, MinecraftKey.getConverter().getGeneric(key));
}
public Object get(String key) {
@@ -251,7 +168,11 @@ public class WrappedRegistry {
}
public MinecraftKey getKey(Object generic) {
return MinecraftKey.getConverter().getSpecific(GET_KEY.invoke(handle, generic));
return MinecraftKey.getConverter().getSpecific(GET_ITEM_KEY.invoke(handle, generic));
}
public ResourceKey getKey() {
return key;
}
public int getId(MinecraftKey key) {
@@ -263,14 +184,10 @@ public class WrappedRegistry {
}
public int getId(Object entry) {
return (int) GET_ID.invoke(this.handle, entry);
return (int) GET_ITEM_ID.invoke(this.handle, entry);
}
public Object getHolder(Object generic) {
if (GET_HOLDER == null) {
throw new UnsupportedOperationException(
"Registry holder conversion is not available on this Minecraft version");
}
return GET_HOLDER.invoke(handle, generic);
}
@@ -286,193 +203,15 @@ public class WrappedRegistry {
return getRegistry(MinecraftReflection.getSoundEffectClass());
}
// Sentinel for negative dynamic-registry lookups. ConcurrentHashMap forbids null
// values, so we cache this to avoid re-walking every fallback registry on every miss.
private static final WrappedRegistry MISS_SENTINEL = new WrappedRegistry(new Object());
public static WrappedRegistry getDataComponentTypeRegistry() {
return getByKey("minecraft:data_component_type");
}
public static WrappedRegistry getRegistry(Class<?> type) {
WrappedRegistry registry = REGISTRY.get(type);
if (registry != null) {
return registry;
}
// Fall back to dynamic (datapack) registry lookup via RegistryAccess
WrappedRegistry cached = DYNAMIC_REGISTRY_CACHE.computeIfAbsent(type, t -> {
Object resourceKey = DYNAMIC_REGISTRY_KEYS.get(t);
WrappedRegistry dynamicRegistry = lookupDynamicRegistry(resourceKey);
if (dynamicRegistry != null) {
return dynamicRegistry;
}
WrappedRegistry byValueType = findDynamicRegistryByValueType(t);
return byValueType != null ? byValueType : MISS_SENTINEL;
});
return cached == MISS_SENTINEL ? null : cached;
return BY_CLASS.get(type);
}
@Nullable
private static WrappedRegistry findDynamicRegistryByValueType(Class<?> type) {
for (Object resourceKey : DYNAMIC_REGISTRY_FALLBACK_KEYS) {
WrappedRegistry registry = lookupDynamicRegistry(resourceKey);
if (registry != null && registryContainsValueType(registry.handle, type)) {
return registry;
}
}
return null;
}
private static boolean registryContainsValueType(Object registryHandle, Class<?> type) {
if (!(registryHandle instanceof Iterable<?> iterable)) {
return false;
}
for (Object entry : iterable) {
if (type.isInstance(entry)) {
return true;
}
}
return false;
}
@Nullable
private static WrappedRegistry lookupDynamicRegistry(@Nullable Object resourceKey) {
if (resourceKey == null || REGISTRY_ACCESS_LOOKUP == null) {
return null;
}
return DYNAMIC_REGISTRY_KEY_CACHE.computeIfAbsent(resourceKey, key -> {
Object registryAccess = MinecraftRegistryAccess.get();
if (registryAccess == null) {
return null;
}
Optional<?> optRegistry = (Optional<?>) REGISTRY_ACCESS_LOOKUP.invoke(registryAccess, key);
if (optRegistry == null || optRegistry.isEmpty()) {
return null;
}
return new WrappedRegistry(optRegistry.get(), key);
});
}
// -------------------------------------------------------------------------
// ResourceKey helpers
// -------------------------------------------------------------------------
/**
* Returns the NMS {@code ResourceKey<Registry<T>>} that identifies this registry,
* or {@code null} if it is not known (e.g. for built-in registries resolved only
* by type scanning).
*/
@Nullable
public Object getRegistryKey() {
return registryKey;
}
/**
* Converts a {@link MinecraftKey} entry location to the corresponding NMS
* {@code ResourceKey<T>} scoped to this registry.
*
* @param key the entry identifier (e.g. {@code minecraft:diamond})
* @return the NMS {@code ResourceKey<T>} handle
* @throws IllegalStateException if this registry's own key is not known
*/
public Object toResourceKey(MinecraftKey key) {
if (registryKey == null) {
throw new IllegalStateException("Registry key is not known for this WrappedRegistry instance");
}
return WrappedResourceKey.of(registryKey, key).getHandle();
}
/**
* Converts a NMS {@code ResourceKey<T>} back to the {@link MinecraftKey}
* representing the entry location.
*
* @param nmsResourceKey the NMS {@code ResourceKey} object
* @return the entry identifier
*/
public static MinecraftKey fromResourceKey(Object nmsResourceKey) {
return WrappedResourceKey.fromHandle(nmsResourceKey).getLocation();
}
/**
* Returns an {@link EquivalentConverter} between
* {@link MinecraftKey} and NMS {@code ResourceKey<T>} for this registry.
*
* @throws IllegalStateException if this registry's own key is not known
*/
public EquivalentConverter<MinecraftKey> resourceKeyConverter() {
if (registryKey == null) {
throw new IllegalStateException("Registry key is not known for this WrappedRegistry instance");
}
return WrappedResourceKey.converterFor(registryKey);
}
/**
* Returns an {@link EquivalentConverter} between
* {@link MinecraftKey} and the NMS registry value type {@code T} (e.g. {@code MenuType<?>},
* {@code SoundEvent}). The converter resolves both directions through this registry's
* {@code get(ResourceLocation)} / {@code getKey(value)} methods.
*
* <p>Use this for fields whose declared NMS type is the registry value itself, as opposed
* to {@code ResourceKey<T>} (use {@link #resourceKeyConverter()}) or {@code Holder<T>}
* (use {@link Converters#holder}).
*/
public EquivalentConverter<MinecraftKey> valueConverter() {
final WrappedRegistry self = this;
return new EquivalentConverter<>() {
@Override
public Object getGeneric(MinecraftKey specific) {
return specific == null ? null : self.get(specific);
}
@Override
public MinecraftKey getSpecific(Object generic) {
return generic == null ? null : self.getKey(generic);
}
@Override
public Class<MinecraftKey> getSpecificType() {
return MinecraftKey.class;
}
};
}
// -------------------------------------------------------------------------
// Named registry accessors
// -------------------------------------------------------------------------
/**
* Returns a {@link WrappedRegistry} backed by the NMS {@code GameRule} registry
* ({@code Registries.GAME_RULE}). The returned instance supports
* {@link #toResourceKey(MinecraftKey)} and {@link #resourceKeyConverter()}.
*
* @return the game-rule registry, or {@code null} if unavailable at this time
*/
@Nullable
public static WrappedRegistry getGameRuleRegistry() {
return getRegistryByNmsKey("core.registries.Registries", "GAME_RULE");
}
/**
* Looks up a registry by the name of a static {@code ResourceKey<Registry<T>>}
* field on the NMS {@code Registries} class.
*
* @param registriesClassName the NMS class name (e.g. {@code "core.registries.Registries"})
* @param fieldName the field name on that class (e.g. {@code "GAME_RULE"})
* @return a {@link WrappedRegistry} with both the handle and the registry key set,
* or {@code null} if the lookup fails
*/
@Nullable
public static WrappedRegistry getRegistryByNmsKey(String registriesClassName, String fieldName) {
try {
Class<?> registriesClass = MinecraftReflection.getMinecraftClass(registriesClassName);
Object regKey = registriesClass.getField(fieldName).get(null);
return lookupDynamicRegistry(regKey);
} catch (Exception ex) {
ProtocolLogger.debug("Failed to resolve registry key " + registriesClassName + "." + fieldName, ex);
}
return null;
public static WrappedRegistry getByKey(String key) {
return BY_KEY.get(key);
}
}
@@ -1,168 +0,0 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.EquivalentConverter;
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 java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* Wrapper around the NMS {@code ResourceKey<T>} class.
*
* <p>A {@code ResourceKey} pairs a registry identifier (e.g. {@code minecraft:item})
* with an entry location (e.g. {@code minecraft:diamond}) to form a fully-qualified
* reference into a Minecraft registry.
*
* <p>All reflective lookups are done with {@link FuzzyReflection} so they are
* independent of Mojang vs. Yarn/intermediary method names.
*/
public final class WrappedResourceKey {
// ResourceKey#location() / ResourceKey#identifier() → ResourceLocation
private static final MethodAccessor GET_LOCATION;
// static ResourceKey#create(ResourceKey<Registry<T>>, ResourceLocation) → ResourceKey<T>
private static final MethodAccessor CREATE;
static {
Class<?> resourceKeyClass = MinecraftReflection.getResourceKey();
Class<?> resourceLocClass = MinecraftReflection.getMinecraftKeyClass();
FuzzyReflection fuzzy = FuzzyReflection.fromClass(resourceKeyClass, false);
// Resolve location() / identifier() by name first (mapping-specific names are known),
// then fall back to fuzzy if neither exists. We must NOT pick registry() by accident —
// that also returns ResourceLocation but is the registry namespace, not the entry path.
MethodAccessor getLocation = null;
for (String candidate : new String[]{"location", "identifier", "getLocation"}) {
try {
Method m = resourceKeyClass.getMethod(candidate);
if (m.getReturnType().equals(resourceLocClass)) {
getLocation = Accessors.getMethodAccessor(m);
break;
}
} catch (NoSuchMethodException ignored) {
}
}
if (getLocation == null) {
// Last-resort fuzzy: pick the method whose simple name contains "location" or "identifier"
getLocation = Accessors.getMethodAccessor(
fuzzy.getMethodList(FuzzyMethodContract.newBuilder()
.parameterCount(0)
.banModifier(Modifier.STATIC)
.returnTypeExact(resourceLocClass)
.build())
.stream()
.filter(m -> {
String n = m.getName().toLowerCase();
return n.contains("location") || n.contains("identifier");
})
.findFirst()
.orElseThrow(() -> new IllegalStateException("Cannot find ResourceKey#location() method"))
);
}
GET_LOCATION = getLocation;
// Find the static two-arg factory: create(ResourceKey<Registry<T>>, ResourceLocation)
CREATE = Accessors.getMethodAccessor(fuzzy.getMethod(FuzzyMethodContract.newBuilder()
.parameterCount(2)
.requireModifier(Modifier.STATIC)
.parameterExactArray(resourceKeyClass, resourceLocClass)
.returnTypeExact(resourceKeyClass)
.build()));
}
private final Object handle;
private WrappedResourceKey(Object handle) {
this.handle = handle;
}
// -------------------------------------------------------------------------
// Factory / unwrap
// -------------------------------------------------------------------------
/**
* Wraps an existing NMS {@code ResourceKey} handle.
*
* @param handle the NMS object; must be an instance of {@code ResourceKey}
* @return a new wrapper
*/
public static WrappedResourceKey fromHandle(Object handle) {
if (handle == null) throw new NullPointerException("handle");
return new WrappedResourceKey(handle);
}
/**
* Creates a new {@code ResourceKey<T>} pointing into the given registry.
*
* @param registryKey the NMS {@code ResourceKey<Registry<T>>} that identifies the registry
* (e.g. the value of {@code Registries.ITEM})
* @param location the entry identifier within that registry
* @return a wrapper around the newly created {@code ResourceKey<T>}
*/
public static WrappedResourceKey of(Object registryKey, MinecraftKey location) {
Object resourceLocation = MinecraftKey.getConverter().getGeneric(location);
return new WrappedResourceKey(CREATE.invoke(null, registryKey, resourceLocation));
}
/**
* Returns the raw NMS {@code ResourceKey} handle.
*/
public Object getHandle() {
return handle;
}
// -------------------------------------------------------------------------
// Accessors
// -------------------------------------------------------------------------
/**
* Returns the entry location part of this key (e.g. {@code minecraft:diamond}).
* This is the {@code ResourceLocation} / {@code Identifier} component, NOT the
* registry identifier.
*/
public MinecraftKey getLocation() {
Object resourceLocation = GET_LOCATION.invoke(handle);
return MinecraftKey.getConverter().getSpecific(resourceLocation);
}
// -------------------------------------------------------------------------
// EquivalentConverter factory
// -------------------------------------------------------------------------
/**
* Returns an {@link EquivalentConverter} that converts
* between {@link MinecraftKey} (the entry location) and a NMS
* {@code ResourceKey<T>} belonging to the supplied registry key.
*
* @param registryKey the NMS {@code ResourceKey<Registry<T>>} (e.g. {@code Registries.ITEM})
*/
public static EquivalentConverter<MinecraftKey> converterFor(Object registryKey) {
return new EquivalentConverter<MinecraftKey>() {
@Override
public MinecraftKey getSpecific(Object generic) {
return WrappedResourceKey.fromHandle(generic).getLocation();
}
@Override
public Object getGeneric(MinecraftKey specific) {
return WrappedResourceKey.of(registryKey, specific).getHandle();
}
@Override
public Class<MinecraftKey> getSpecificType() {
return MinecraftKey.class;
}
};
}
@Override
public String toString() {
return "WrappedResourceKey{" + handle + "}";
}
}
@@ -1,122 +0,0 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.reflect.EquivalentConverter;
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.utility.MinecraftReflection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* Wrapper for {@code TagNetworkSerialization.NetworkPayload}.
*/
public final class WrappedTagPayload {
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getMinecraftClass(
"tags.TagNetworkSerialization$NetworkPayload");
private static final ConstructorAccessor CONSTRUCTOR = Accessors.getConstructorAccessor(HANDLE_TYPE, Map.class);
private static final FieldAccessor TAGS = Accessors.getFieldAccessor(HANDLE_TYPE, Map.class, true);
private static final ConstructorAccessor INT_ARRAY_LIST_CONSTRUCTOR =
Accessors.getConstructorAccessor(MinecraftReflection.getIntArrayListClass(), Collection.class);
private static final MethodAccessor CREATE_REGISTRY_KEY = Accessors.getMethodAccessor(
MinecraftReflection.getResourceKey(),
"createRegistryKey",
MinecraftReflection.getMinecraftKeyClass());
public static final EquivalentConverter<MinecraftKey> REGISTRY_KEY_CONVERTER =
Converters.ignoreNull(new EquivalentConverter<>() {
@Override
public Object getGeneric(MinecraftKey specific) {
return CREATE_REGISTRY_KEY.invoke(null, MinecraftKey.getConverter().getGeneric(specific));
}
@Override
public MinecraftKey getSpecific(Object generic) {
return WrappedResourceKey.fromHandle(generic).getLocation();
}
@Override
public Class<MinecraftKey> getSpecificType() {
return MinecraftKey.class;
}
});
public static final EquivalentConverter<WrappedTagPayload> CONVERTER =
Converters.ignoreNull(new EquivalentConverter<>() {
@Override
public Object getGeneric(WrappedTagPayload specific) {
Map<Object, Object> tags = new HashMap<>();
for (Map.Entry<MinecraftKey, List<Integer>> entry : specific.tags.entrySet()) {
tags.put(
MinecraftKey.getConverter().getGeneric(entry.getKey()),
INT_ARRAY_LIST_CONSTRUCTOR.invoke(entry.getValue()));
}
return CONSTRUCTOR.invoke(tags);
}
@Override
public WrappedTagPayload getSpecific(Object generic) {
Map<?, ?> genericTags = (Map<?, ?>) TAGS.get(generic);
Map<MinecraftKey, List<Integer>> tags = new HashMap<>();
for (Map.Entry<?, ?> entry : genericTags.entrySet()) {
List<Integer> ids = new ArrayList<>();
for (Object id : (Iterable<?>) entry.getValue()) {
ids.add((Integer) id);
}
tags.put(MinecraftKey.getConverter().getSpecific(entry.getKey()), ids);
}
return new WrappedTagPayload(tags);
}
@Override
public Class<WrappedTagPayload> getSpecificType() {
return WrappedTagPayload.class;
}
});
private final Map<MinecraftKey, List<Integer>> tags;
public WrappedTagPayload(Map<MinecraftKey, List<Integer>> tags) {
this.tags = copy(tags);
}
public Map<MinecraftKey, List<Integer>> getTags() {
return copy(tags);
}
private static Map<MinecraftKey, List<Integer>> copy(Map<MinecraftKey, List<Integer>> tags) {
Map<MinecraftKey, List<Integer>> copy = new HashMap<>();
for (Map.Entry<MinecraftKey, List<Integer>> entry : tags.entrySet()) {
copy.put(entry.getKey(), List.copyOf(entry.getValue()));
}
return copy;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof WrappedTagPayload that)) {
return false;
}
return Objects.equals(tags, that.tags);
}
@Override
public int hashCode() {
return Objects.hash(tags);
}
@Override
public String toString() {
return "WrappedTagPayload{tags=" + tags + '}';
}
}
@@ -3,8 +3,6 @@ package com.comphenix.protocol.wrappers;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import com.comphenix.protocol.injector.StructureCache;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
@@ -92,26 +90,12 @@ public class WrappedTeamParameters extends AbstractWrapper {
@NotNull
public EnumWrappers.ChatFormatting getColor() {
if (MinecraftVersion.v26_2.atOrAbove()) {
Optional<?> optional = modifier.<Optional<?>>withType(Optional.class).read(0);
Object teamColor = optional != null ? optional.orElse(null) : null;
if (teamColor == null) {
return EnumWrappers.ChatFormatting.RESET;
}
return EnumWrappers.ChatFormatting.valueOf(((Enum<?>) teamColor).name());
}
return modifier
.withType(EnumWrappers.getChatFormattingClass(), EnumWrappers.getChatFormattingConverter())
.read(0);
}
public int getOptions() {
if (MinecraftVersion.v26_2.atOrAbove()) {
return (byte) modifier.withType(byte.class).read(0);
}
return (int) modifier.withType(int.class).read(0);
}
@@ -226,27 +210,9 @@ public class WrappedTeamParameters extends AbstractWrapper {
wrapped.modifier.withType(String.class).writeSafely(1, collisionRule.toString());
}
if (MinecraftVersion.v26_2.atOrAbove()) {
wrapped.modifier.withType(Optional.class).write(0, Optional.ofNullable(toNmsTeamColor(color)));
wrapped.modifier.withType(byte.class).write(0, (byte) options);
} else {
wrapped.modifier.withType(EnumWrappers.getChatFormattingClass()).write(0, EnumWrappers.getChatFormattingConverter().getGeneric(color));
wrapped.modifier.withType(int.class).write(0, options);
}
wrapped.modifier.withType(EnumWrappers.getChatFormattingClass()).write(0, EnumWrappers.getChatFormattingConverter().getGeneric(color));
wrapped.modifier.withType(int.class).write(0, options);
return wrapped;
}
@SuppressWarnings({"unchecked", "rawtypes"})
private static Object toNmsTeamColor(EnumWrappers.ChatFormatting color) {
Class<?> teamColorClass = MinecraftReflection.getTeamColorClass()
.orElseThrow(() -> new IllegalStateException("TeamColor class doesn't exist on this server version"));
try {
return Enum.valueOf((Class) teamColorClass, color.name());
} catch (IllegalArgumentException ex) {
// formatting-only values (e.g. RESET) don't map to a team color
return null;
}
}
}
}
@@ -1,194 +0,0 @@
package com.comphenix.protocol.wrappers;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Wraps the NMS {@code WeightedList<E>} type, representing a list of weighted
* entries where each entry has a value and an integer weight.
*
* <p>The element type {@code T} is the <em>Bukkit / ProtocolLib</em> representation
* of the NMS element type, converted via the supplied {@link EquivalentConverter}.
*
* @param <T> the Bukkit-side element type
*/
public class WrappedWeightedList<T> {
// ── NMS reflection handles (eagerly resolved at class load) ──────────
private static final Class<?> WEIGHTED_LIST_CLASS;
private static final Class<?> WEIGHTED_CLASS;
private static final MethodAccessor WEIGHTED_LIST_OF; // static WeightedList.of(List<Weighted>)
private static final MethodAccessor WEIGHTED_LIST_UNWRAP; // WeightedList.unwrap() → List<Weighted>
private static final ConstructorAccessor WEIGHTED_CTOR; // new Weighted(Object value, int weight)
private static final MethodAccessor WEIGHTED_VALUE; // Weighted.value()
private static final MethodAccessor WEIGHTED_WEIGHT; // Weighted.weight()
static {
WEIGHTED_LIST_CLASS = MinecraftReflection.getMinecraftClass("util.random.WeightedList");
WEIGHTED_CLASS = MinecraftReflection.getMinecraftClass("util.random.Weighted");
WEIGHTED_LIST_OF = Accessors.getMethodAccessor(WEIGHTED_LIST_CLASS, "of", List.class);
WEIGHTED_LIST_UNWRAP = Accessors.getMethodAccessor(WEIGHTED_LIST_CLASS, "unwrap");
WEIGHTED_CTOR = Accessors.getConstructorAccessor(WEIGHTED_CLASS, Object.class, int.class);
WEIGHTED_VALUE = Accessors.getMethodAccessor(WEIGHTED_CLASS, "value");
WEIGHTED_WEIGHT = Accessors.getMethodAccessor(WEIGHTED_CLASS, "weight");
}
/**
* Returns the NMS {@code WeightedList} class.
*/
public static Class<?> getNmsClass() {
return WEIGHTED_LIST_CLASS;
}
// ── Instance data ────────────────────────────────────────────────────
private final List<Entry<T>> entries;
public WrappedWeightedList() {
this.entries = new ArrayList<>();
}
public WrappedWeightedList(List<Entry<T>> entries) {
this.entries = new ArrayList<>(entries);
}
/**
* Returns a mutable view of the entries in this weighted list. Mutations are
* reflected on this wrapper.
*/
public List<Entry<T>> getEntries() {
return entries;
}
/**
* Replaces all entries with the given list. The argument is copied; subsequent
* modifications to it are not reflected on this wrapper.
*/
public void setEntries(List<Entry<T>> entries) {
this.entries.clear();
this.entries.addAll(entries);
}
/**
* Appends a single entry.
*/
public void addEntry(Entry<T> entry) {
this.entries.add(entry);
}
/**
* Appends a single value/weight entry.
*/
public void addEntry(T value, int weight) {
this.entries.add(new Entry<>(value, weight));
}
// ── Entry record ─────────────────────────────────────────────────────
/**
* A single weighted entry pairing a value with an integer weight.
*
* @param <T> the Bukkit-side element type
*/
public record Entry<T>(T value, int weight) {
/** Backwards-compatible accessor for callers that used {@code getValue()}. */
public T getValue() {
return value;
}
/** Backwards-compatible accessor for callers that used {@code getWeight()}. */
public int getWeight() {
return weight;
}
}
// ── Converter factory ────────────────────────────────────────────────
/**
* Creates an {@link EquivalentConverter} that converts between
* {@code WrappedWeightedList<T>} and the NMS {@code WeightedList<E>}.
*
* @param elementConverter converter between the Bukkit type {@code T}
* and the NMS element type {@code E}
* @param <T> Bukkit-side element type
* @return the converter
*/
public static <T> EquivalentConverter<WrappedWeightedList<T>> getConverter(
EquivalentConverter<T> elementConverter) {
return new EquivalentConverter<>() {
@Override
@SuppressWarnings("unchecked")
public WrappedWeightedList<T> getSpecific(Object generic) {
List<Object> nmsWeightedEntries = (List<Object>) WEIGHTED_LIST_UNWRAP.invoke(generic);
List<Entry<T>> entries = new ArrayList<>(nmsWeightedEntries.size());
for (Object nmsWeighted : nmsWeightedEntries) {
Object nmsValue = WEIGHTED_VALUE.invoke(nmsWeighted);
int weight = (int) WEIGHTED_WEIGHT.invoke(nmsWeighted);
T value = elementConverter.getSpecific(nmsValue);
entries.add(new Entry<>(value, weight));
}
return new WrappedWeightedList<>(entries);
}
@Override
public Object getGeneric(WrappedWeightedList<T> specific) {
List<Object> nmsWeightedEntries = new ArrayList<>(specific.entries.size());
for (Entry<T> entry : specific.entries) {
Object nmsValue = elementConverter.getGeneric(entry.getValue());
Object nmsWeighted = WEIGHTED_CTOR.invoke(nmsValue, entry.getWeight());
nmsWeightedEntries.add(nmsWeighted);
}
return WEIGHTED_LIST_OF.invoke(null, nmsWeightedEntries);
}
@Override
@SuppressWarnings("unchecked")
public Class<WrappedWeightedList<T>> getSpecificType() {
return (Class<WrappedWeightedList<T>>) (Class<?>) WrappedWeightedList.class;
}
};
}
// ── Convenience factories ────────────────────────────────────────────
/**
* Creates an empty weighted list.
*/
public static <T> WrappedWeightedList<T> empty() {
return new WrappedWeightedList<>(Collections.emptyList());
}
// ── Object methods ───────────────────────────────────────────────────
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof WrappedWeightedList<?> that)) return false;
return Objects.equals(entries, that.entries);
}
@Override
public int hashCode() {
return Objects.hashCode(entries);
}
@Override
public String toString() {
return "WrappedWeightedList{entries=" + entries + "}";
}
}
@@ -5,7 +5,8 @@ import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Base64;
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
import com.comphenix.protocol.wrappers.nbt.NbtBase;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
@@ -22,7 +23,7 @@ public class NbtTextSerializer {
* A default instance of this serializer.
*/
public static final NbtTextSerializer DEFAULT = new NbtTextSerializer();
private NbtBinarySerializer binarySerializer;
public NbtTextSerializer() {
@@ -58,7 +59,7 @@ public class NbtTextSerializer {
binarySerializer.serialize(value, dataOutput);
// Serialize that array
return Base64.getEncoder().encodeToString(outputStream.toByteArray());
return Base64Coder.encodeLines(outputStream.toByteArray());
}
/**
@@ -69,7 +70,7 @@ public class NbtTextSerializer {
* @throws IOException If we are unable to parse the input.
*/
public <TType> NbtWrapper<TType> deserialize(String input) throws IOException {
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(input));
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(input));
return binarySerializer.deserialize(new DataInputStream(inputStream));
}
@@ -7,8 +7,6 @@ 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.DefaultInstances;
import com.comphenix.protocol.reflect.instances.MinecraftGenerator;
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
@@ -25,7 +23,6 @@ public final class ServerPingRecord implements ServerPingImpl {
private static Class<?> SERVER_PING;
private static Class<?> PLAYER_SAMPLE_CLASS;
private static Class<?> SERVER_DATA_CLASS;
private static Class<?> NAME_AND_ID_CLASS;
private static Class<?> GSON_CLASS;
private static MethodAccessor GSON_TO_JSON;
@@ -59,37 +56,13 @@ public final class ServerPingRecord implements ServerPingImpl {
PLAYER_SAMPLE_CLASS = MinecraftReflection.getServerPingPlayerSampleClass();
SERVER_DATA_CLASS = MinecraftReflection.getServerPingServerDataClass();
PING_CTOR = Accessors.getConstructorAccessor(SERVER_PING, MinecraftReflection.getIChatBaseComponentClass(), Optional.class, Optional.class, Optional.class, boolean.class);
PING_CTOR = Accessors.getConstructorAccessor(SERVER_PING.getConstructors()[0]);
DATA_WRAPPER = AutoWrapper.wrap(ServerData.class, SERVER_DATA_CLASS);
SAMPLE_WRAPPER = AutoWrapper.wrap(PlayerSample.class, PLAYER_SAMPLE_CLASS);
FAVICON_WRAPPER = AutoWrapper.wrap(Favicon.class, MinecraftReflection.getMinecraftClass("network.protocol.status.ServerPing$a", "network.protocol.status.ServerStatus$Favicon"));
if (MinecraftVersion.v1_21_10.atOrAbove()) {
NAME_AND_ID_CLASS = MinecraftReflection.getMinecraftClass("server.players.NameAndId");
NAME_AND_ID_WRAPPER = AutoWrapper.wrap(NameAndId.class, NAME_AND_ID_CLASS);
PROFILE_LIST_CONVERTER = BukkitConverters.getListConverter(new EquivalentConverter<>() {
@Override
public Object getGeneric(WrappedGameProfile specific) {
NameAndId wrapper = new NameAndId(specific.getUUID(), specific.getName());
return NAME_AND_ID_WRAPPER.getGeneric(wrapper);
}
@Override
public WrappedGameProfile getSpecific(Object generic) {
NameAndId wrapper = NAME_AND_ID_WRAPPER.getSpecific(generic);
return new WrappedGameProfile(wrapper.id, wrapper.name);
}
@Override
public Class<WrappedGameProfile> getSpecificType() {
return WrappedGameProfile.class;
}
});
} else {
PROFILE_LIST_CONVERTER = BukkitConverters.getListConverter(BukkitConverters.getWrappedGameProfileConverter());
}
PROFILE_LIST_CONVERTER = BukkitConverters.getListConverter(BukkitConverters.getWrappedGameProfileConverter());
DEFAULT_DESCRIPTION = WrappedChatComponent.fromLegacyText("A Minecraft Server");
@@ -151,24 +124,11 @@ public final class ServerPingRecord implements ServerPingImpl {
}
}
public static final class NameAndId {
public UUID id;
public String name;
public NameAndId(UUID id, String name) {
this.id = id;
this.name = name;
}
public NameAndId() {
this(MinecraftGenerator.SYS_UUID, "");
}
}
private static AutoWrapper<PlayerSample> SAMPLE_WRAPPER;
private static AutoWrapper<ServerData> DATA_WRAPPER;
private static AutoWrapper<Favicon> FAVICON_WRAPPER;
private static AutoWrapper<NameAndId> NAME_AND_ID_WRAPPER;
private WrappedChatComponent description;
private PlayerSample playerSample;
@@ -1,69 +0,0 @@
package net.dmulloy2.protocol;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketContainer;
import java.util.Objects;
import org.bukkit.entity.Player;
/**
* Base class for all packet wrappers. Provides access to the underlying
* {@link PacketContainer} and convenience methods for sending/receiving.
*/
public abstract class AbstractPacket {
protected final PacketContainer handle;
protected AbstractPacket(PacketContainer handle, PacketType type) {
if (handle == null) {
throw new IllegalArgumentException("Packet handle cannot be null");
}
if (type == null) {
throw new IllegalArgumentException("Packet type cannot be null");
}
if (!handle.getType().equals(type)) {
throw new IllegalArgumentException(
"Invalid packet type: expected " + type + ", got " + handle.getType());
}
this.handle = handle;
}
/**
* Returns the underlying {@link PacketContainer}.
*/
public PacketContainer getHandle() {
return handle;
}
/**
* Sends this packet to a player (server → client).
*/
public void sendPacket(Player receiver) {
ProtocolLibrary.getProtocolManager().sendServerPacket(receiver, getHandle());
}
/**
* Simulates receiving this packet from a player (client → server).
*/
public void receivePacket(Player player) {
ProtocolLibrary.getProtocolManager().receiveClientPacket(player, getHandle());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AbstractPacket other = (AbstractPacket) o;
return Objects.equals(handle, other.handle);
}
@Override
public int hashCode() {
return Objects.hashCode(handle);
}
@Override
public String toString() {
return getClass().getSimpleName() + "{handle=" + handle + "}";
}
}
@@ -1,36 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.clientbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.EquivalentConstructor;
import net.dmulloy2.protocol.AbstractPacket;
/**
* Wrapper for {@code ClientboundCodeOfConductPacket} (configuration phase, clientbound).
*/
public class WrappedClientboundCodeOfConductPacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Server.CODE_OF_CONDUCT;
private static final EquivalentConstructor CONSTRUCTOR = new EquivalentConstructor(TYPE)
.withParam(String.class);
public WrappedClientboundCodeOfConductPacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedClientboundCodeOfConductPacket(String codeOfConduct) {
this(PacketContainer.fromPacket(CONSTRUCTOR.create(codeOfConduct)));
}
public WrappedClientboundCodeOfConductPacket(PacketContainer packet) {
super(packet, TYPE);
}
public String getCodeOfConduct() {
return handle.getStrings().read(0);
}
public void setCodeOfConduct(String codeOfConduct) {
handle.getStrings().write(0, codeOfConduct);
}
}
@@ -1,43 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.clientbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.EquivalentConstructor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.MinecraftKey;
import net.dmulloy2.protocol.AbstractPacket;
/**
* Wrapper for {@code ClientboundCookieRequestPacket} (configuration phase, clientbound).
*
* <p>Packet structure:
* <ul>
* <li>{@code Identifier key} – namespaced key identifying the cookie to request</li>
* </ul>
*/
public class WrappedClientboundCookieRequestPacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Server.COOKIE_REQUEST;
private static final EquivalentConstructor CONSTRUCTOR = new EquivalentConstructor(TYPE)
.withParam(MinecraftReflection.getMinecraftKeyClass(), MinecraftKey.getConverter());
public WrappedClientboundCookieRequestPacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedClientboundCookieRequestPacket(MinecraftKey key) {
this(new PacketContainer(TYPE, CONSTRUCTOR.create(key)));
}
public WrappedClientboundCookieRequestPacket(PacketContainer packet) {
super(packet, TYPE);
}
public MinecraftKey getKey() {
return handle.getMinecraftKeys().read(0);
}
public void setKey(MinecraftKey key) {
handle.getMinecraftKeys().write(0, key);
}
}
@@ -1,42 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.clientbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.EquivalentConstructor;
import com.comphenix.protocol.wrappers.CustomPacketPayloadWrapper;
import net.dmulloy2.protocol.AbstractPacket;
/**
* Wrapper for {@code ClientboundCustomPayloadPacket} (configuration phase, clientbound).
*
* <p>Packet structure:
* <ul>
* <li>{@code CustomPacketPayload payload} – channel-specific payload</li>
* </ul>
*/
public class WrappedClientboundCustomPayloadPacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Server.CUSTOM_PAYLOAD;
private static final EquivalentConstructor CONSTRUCTOR = new EquivalentConstructor(TYPE)
.withParam(CustomPacketPayloadWrapper.getCustomPacketPayloadClass(), CustomPacketPayloadWrapper.getConverter());
public WrappedClientboundCustomPayloadPacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedClientboundCustomPayloadPacket(CustomPacketPayloadWrapper payload) {
this(new PacketContainer(TYPE, CONSTRUCTOR.create(payload)));
}
public WrappedClientboundCustomPayloadPacket(PacketContainer packet) {
super(packet, TYPE);
}
public CustomPacketPayloadWrapper getPayload() {
return handle.getCustomPacketPayloads().read(0);
}
public void setPayload(CustomPacketPayloadWrapper payload) {
handle.getCustomPacketPayloads().write(0, payload);
}
}
@@ -1,48 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.clientbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.EquivalentConstructor;
import com.comphenix.protocol.wrappers.Converters;
import java.util.Map;
import net.dmulloy2.protocol.AbstractPacket;
/**
* Wrapper for {@code ClientboundCustomReportDetailsPacket} (configuration phase, clientbound).
*
* <p>Packet structure:
* <ul>
* <li>{@code Map<String, String> details} – custom crash report detail entries</li>
* </ul>
*/
public class WrappedClientboundCustomReportDetailsPacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Server.REPORT_DETAILS;
private static final EquivalentConstructor CONSTRUCTOR = new EquivalentConstructor(TYPE)
.withParam(Map.class);
public WrappedClientboundCustomReportDetailsPacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedClientboundCustomReportDetailsPacket(Map<String, String> details) {
this(new PacketContainer(TYPE, CONSTRUCTOR.create(details)));
}
public WrappedClientboundCustomReportDetailsPacket(PacketContainer packet) {
super(packet, TYPE);
}
public Map<String, String> getDetails() {
return handle.getMaps(
Converters.passthrough(String.class),
Converters.passthrough(String.class)).readSafely(0);
}
public void setDetails(Map<String, String> details) {
handle.getMaps(
Converters.passthrough(String.class),
Converters.passthrough(String.class)).writeSafely(0, details);
}
}
@@ -1,39 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.clientbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.EquivalentConstructor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import net.dmulloy2.protocol.AbstractPacket;
/**
* Wrapper for {@code ClientboundDisconnectPacket} (configuration phase, clientbound).
*/
public class WrappedClientboundDisconnectPacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Server.DISCONNECT;
private static final EquivalentConstructor CONSTRUCTOR = new EquivalentConstructor(TYPE)
.withParam(MinecraftReflection.getIChatBaseComponentClass(), BukkitConverters.getWrappedChatComponentConverter());
public WrappedClientboundDisconnectPacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedClientboundDisconnectPacket(WrappedChatComponent reason) {
this(new PacketContainer(TYPE, CONSTRUCTOR.create(reason)));
}
public WrappedClientboundDisconnectPacket(PacketContainer packet) {
super(packet, TYPE);
}
public WrappedChatComponent getReason() {
return handle.getChatComponents().read(0);
}
public void setReason(WrappedChatComponent reason) {
handle.getChatComponents().write(0, reason);
}
}
@@ -1,36 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.clientbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.EquivalentConstructor;
import net.dmulloy2.protocol.AbstractPacket;
/**
* Wrapper for {@code ClientboundKeepAlivePacket} (configuration phase, clientbound).
*/
public class WrappedClientboundKeepAlivePacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Server.KEEP_ALIVE;
private static final EquivalentConstructor CONSTRUCTOR = new EquivalentConstructor(TYPE)
.withParam(long.class);
public WrappedClientboundKeepAlivePacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedClientboundKeepAlivePacket(long id) {
this(new PacketContainer(TYPE, CONSTRUCTOR.create(id)));
}
public WrappedClientboundKeepAlivePacket(PacketContainer packet) {
super(packet, TYPE);
}
public long getId() {
return handle.getLongs().read(0);
}
public void setId(long id) {
handle.getLongs().write(0, id);
}
}
@@ -1,36 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.clientbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.EquivalentConstructor;
import net.dmulloy2.protocol.AbstractPacket;
/**
* Wrapper for {@code ClientboundPingPacket} (configuration phase, clientbound).
*/
public class WrappedClientboundPingPacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Server.PING;
private static final EquivalentConstructor CONSTRUCTOR = new EquivalentConstructor(TYPE)
.withParam(int.class);
public WrappedClientboundPingPacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedClientboundPingPacket(int id) {
this(new PacketContainer(TYPE, CONSTRUCTOR.create(id)));
}
public WrappedClientboundPingPacket(PacketContainer packet) {
super(packet, TYPE);
}
public int getId() {
return handle.getIntegers().read(0);
}
public void setId(int id) {
handle.getIntegers().write(0, id);
}
}
@@ -1,27 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.clientbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import net.dmulloy2.protocol.AbstractPacket;
/**
* Wrapper for {@code ClientboundRegistryDataPacket} (configuration phase, clientbound).
*/
public class WrappedClientboundRegistryDataPacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Server.REGISTRY_DATA;
public WrappedClientboundRegistryDataPacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedClientboundRegistryDataPacket(PacketContainer packet) {
super(packet, TYPE);
}
// TODO: missing field 'registry' (NMS type: ResourceKey<? extends Registry<?>>)
// Use getMinecraftKeys().read(0) for the registry key identifier, or getSpecificModifier(ResourceKey.class).read(0).
// TODO: missing field 'entries' (NMS type: List<RegistrySynchronization.PackedRegistryEntry>)
// Each entry has an id (Identifier) and an optional NBT tag. Use handle.getModifier().read(1)
// for the raw List, or add a dedicated WrappedPackedRegistryEntry class.
}
@@ -1,45 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.clientbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.EquivalentConstructor;
import com.comphenix.protocol.wrappers.Converters;
import java.util.Optional;
import java.util.UUID;
import net.dmulloy2.protocol.AbstractPacket;
/**
* Wrapper for {@code ClientboundResourcePackPopPacket} (configuration phase, clientbound).
*
* <p>Packet structure:
* <ul>
* <li>{@code Optional<UUID> id} – UUID of the resource pack to remove, or empty to remove all</li>
* </ul>
*/
public class WrappedClientboundResourcePackPopPacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Server.REMOVE_RESOURCE_PACK;
private static final EquivalentConstructor CONSTRUCTOR = new EquivalentConstructor(TYPE)
.withParam(Optional.class);
public WrappedClientboundResourcePackPopPacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedClientboundResourcePackPopPacket(Optional<UUID> id) {
this(new PacketContainer(TYPE, CONSTRUCTOR.create(id)));
}
public WrappedClientboundResourcePackPopPacket(PacketContainer packet) {
super(packet, TYPE);
}
public Optional<UUID> getId() {
return handle.getOptionals(Converters.passthrough(UUID.class)).readSafely(0);
}
public void setId(Optional<UUID> id) {
handle.getOptionals(Converters.passthrough(UUID.class)).writeSafely(0, id);
}
}
@@ -1,79 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.clientbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.EquivalentConstructor;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.Converters;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import java.util.Optional;
import java.util.UUID;
import net.dmulloy2.protocol.AbstractPacket;
/**
* Wrapper for {@code ClientboundResourcePackPushPacket} (configuration phase, clientbound).
*/
public class WrappedClientboundResourcePackPushPacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Server.ADD_RESOURCE_PACK;
// NMS constructor order: (UUID id, String url, String hash, boolean required, Optional<Component> prompt)
private static final EquivalentConstructor CONSTRUCTOR = new EquivalentConstructor(TYPE)
.withParam(UUID.class)
.withParam(String.class)
.withParam(String.class)
.withParam(boolean.class)
.withParam(Optional.class, Converters.optional(BukkitConverters.getWrappedChatComponentConverter()));
public WrappedClientboundResourcePackPushPacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedClientboundResourcePackPushPacket(String url, String hash, boolean required, UUID id, Optional<WrappedChatComponent> prompt) {
this(new PacketContainer(TYPE, CONSTRUCTOR.create(id, url, hash, required, prompt)));
}
public WrappedClientboundResourcePackPushPacket(PacketContainer packet) {
super(packet, TYPE);
}
public String getUrl() {
return handle.getStrings().read(0);
}
public void setUrl(String url) {
handle.getStrings().write(0, url);
}
public String getHash() {
return handle.getStrings().read(1);
}
public void setHash(String hash) {
handle.getStrings().write(1, hash);
}
public boolean isRequired() {
return handle.getBooleans().read(0);
}
public void setRequired(boolean required) {
handle.getBooleans().write(0, required);
}
public UUID getId() {
return handle.getUUIDs().read(0);
}
public void setId(UUID id) {
handle.getUUIDs().write(0, id);
}
public Optional<WrappedChatComponent> getPrompt() {
return handle.getOptionals(BukkitConverters.getWrappedChatComponentConverter()).read(0);
}
public void setPrompt(Optional<WrappedChatComponent> prompt) {
handle.getOptionals(BukkitConverters.getWrappedChatComponentConverter()).write(0, prompt);
}
}
@@ -1,42 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.clientbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.EquivalentConstructor;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.WrappedKnownPack;
import net.dmulloy2.protocol.AbstractPacket;
import java.util.List;
/**
* Wrapper for {@code ClientboundSelectKnownPacks} (configuration phase, clientbound).
* Selects known packs.
*/
public class WrappedClientboundSelectKnownPacksPacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Server.SELECT_KNOWN_PACKS;
private static final EquivalentConstructor CONSTRUCTOR = new EquivalentConstructor(TYPE)
.withParam(List.class, BukkitConverters.getListConverter(WrappedKnownPack.CONVERTER));
public WrappedClientboundSelectKnownPacksPacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedClientboundSelectKnownPacksPacket(List<WrappedKnownPack> knownPacks) {
this(new PacketContainer(TYPE, CONSTRUCTOR.create(knownPacks)));
}
public WrappedClientboundSelectKnownPacksPacket(PacketContainer packet) {
super(packet, TYPE);
}
public List<WrappedKnownPack> getKnownPacks() {
return handle.getLists(WrappedKnownPack.CONVERTER).read(0);
}
public void setKnownPacks(List<WrappedKnownPack> knownPacks) {
handle.getLists(WrappedKnownPack.CONVERTER).write(0, knownPacks);
}
}
@@ -1,84 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.clientbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.AutoWrapper;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.Either;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import net.dmulloy2.protocol.AbstractPacket;
import java.util.List;
/**
* Wrapper for {@code ClientboundServerLinksPacket} (configuration phase, clientbound).
*
* <p>Fields:
* <ul>
* <li>{@code List<ServerLinks.UntrustedEntry> links} – server-provided links</li>
* </ul>
*
* <p>NMS: {@code record UntrustedEntry(Either<KnownLinkType, Component> type, String link)}
*/
public class WrappedClientboundServerLinksPacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Server.SERVER_LINKS;
/** Mirrors NMS {@code ServerLinks.KnownLinkType}; constant order must match. */
public enum KnownLinkType {
BUG_REPORT, COMMUNITY_GUIDELINES, SUPPORT, STATUS, FEEDBACK,
COMMUNITY, WEBSITE, FORUMS, NEWS, ANNOUNCEMENTS
}
private static final Class<?> NMS_KNOWN_LINK_TYPE_CLASS =
MinecraftReflection.getMinecraftClass("server.ServerLinks$KnownLinkType");
private static final EquivalentConverter<Either<KnownLinkType, WrappedChatComponent>> TYPE_CONVERTER =
BukkitConverters.getEitherConverter(
EnumWrappers.getGenericConverter(NMS_KNOWN_LINK_TYPE_CLASS, KnownLinkType.class),
BukkitConverters.getWrappedChatComponentConverter());
private static final AutoWrapper<WrappedUntrustedEntry> ENTRY_WRAPPER =
AutoWrapper.wrap(WrappedUntrustedEntry.class, "server.ServerLinks$UntrustedEntry")
.field(0, TYPE_CONVERTER);
public WrappedClientboundServerLinksPacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedClientboundServerLinksPacket(PacketContainer packet) {
super(packet, TYPE);
}
/**
* Returns the list of server links.
*/
public List<WrappedUntrustedEntry> getLinks() {
List<WrappedUntrustedEntry> links = handle.getLists(ENTRY_WRAPPER).read(0);
return links != null ? links : List.of();
}
public void setLinks(List<WrappedUntrustedEntry> links) {
handle.getLists(ENTRY_WRAPPER).write(0, links);
}
/**
* POJO mirroring {@code record ServerLinks.UntrustedEntry(Either<KnownLinkType, Component> type, String link)}.
*/
public static final class WrappedUntrustedEntry {
/** Either a {@link KnownLinkType} (left) or a custom {@link WrappedChatComponent} display name (right). */
public Either<KnownLinkType, WrappedChatComponent> type;
/** The URL string. */
public String link;
public WrappedUntrustedEntry() {}
public WrappedUntrustedEntry(Either<KnownLinkType, WrappedChatComponent> type, String link) {
this.type = type;
this.link = link;
}
}
}
@@ -1,25 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.clientbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import net.dmulloy2.protocol.AbstractPacket;
/**
* Wrapper for {@code ClientboundShowDialogPacket} (configuration phase, clientbound).
* Sends a dialog to the client.
*/
public class WrappedClientboundShowDialogPacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Server.SHOW_DIALOG;
public WrappedClientboundShowDialogPacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedClientboundShowDialogPacket(PacketContainer packet) {
super(packet, TYPE);
}
// TODO: missing field 'dialog' (NMS type: Holder<Dialog> — registry-backed dialog definition)
// Same as the game-phase ShowDialog packet. Use handle.getModifier().read(0) for the raw Holder<Dialog>.
}
@@ -1,53 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.clientbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.EquivalentConstructor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.MinecraftKey;
import net.dmulloy2.protocol.AbstractPacket;
/**
* Wrapper for {@code ClientboundStoreCookiePacket} (configuration phase, clientbound).
*
* <p>Packet structure:
* <ul>
* <li>{@code Identifier key} – namespaced key identifying the cookie</li>
* <li>{@code byte[] payload} – cookie data (max 5120 bytes)</li>
* </ul>
*/
public class WrappedClientboundStoreCookiePacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Server.STORE_COOKIE;
private static final EquivalentConstructor CONSTRUCTOR = new EquivalentConstructor(TYPE)
.withParam(MinecraftReflection.getMinecraftKeyClass(), MinecraftKey.getConverter())
.withParam(byte[].class);
public WrappedClientboundStoreCookiePacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedClientboundStoreCookiePacket(MinecraftKey key, byte[] payload) {
this(new PacketContainer(TYPE, CONSTRUCTOR.create(key, payload)));
}
public WrappedClientboundStoreCookiePacket(PacketContainer packet) {
super(packet, TYPE);
}
public MinecraftKey getKey() {
return handle.getMinecraftKeys().read(0);
}
public void setKey(MinecraftKey key) {
handle.getMinecraftKeys().write(0, key);
}
public byte[] getPayload() {
return handle.getByteArrays().read(0);
}
public void setPayload(byte[] payload) {
handle.getByteArrays().write(0, payload);
}
}
@@ -1,45 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.clientbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.EquivalentConstructor;
import net.dmulloy2.protocol.AbstractPacket;
/**
* Wrapper for {@code ClientboundTransferPacket} (configuration phase, clientbound).
*/
public class WrappedClientboundTransferPacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Server.TRANSFER;
private static final EquivalentConstructor CONSTRUCTOR = new EquivalentConstructor(TYPE)
.withParam(String.class)
.withParam(int.class);
public WrappedClientboundTransferPacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedClientboundTransferPacket(String host, int port) {
this(new PacketContainer(TYPE, CONSTRUCTOR.create(host, port)));
}
public WrappedClientboundTransferPacket(PacketContainer packet) {
super(packet, TYPE);
}
public String getHost() {
return handle.getStrings().read(0);
}
public void setHost(String host) {
handle.getStrings().write(0, host);
}
public int getPort() {
return handle.getIntegers().read(0);
}
public void setPort(int port) {
handle.getIntegers().write(0, port);
}
}
@@ -1,45 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.clientbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.EquivalentConstructor;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.MinecraftKey;
import java.util.Set;
import net.dmulloy2.protocol.AbstractPacket;
/**
* Wrapper for {@code ClientboundUpdateEnabledFeaturesPacket} (configuration phase, clientbound).
*
* <p>Packet structure:
* <ul>
* <li>{@code Set<ResourceLocation> features} – set of enabled feature flag identifiers</li>
* </ul>
*/
public class WrappedClientboundUpdateEnabledFeaturesPacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Server.UPDATE_ENABLED_FEATURES;
private static final EquivalentConstructor CONSTRUCTOR = new EquivalentConstructor(TYPE)
.withParam(Set.class, BukkitConverters.getSetConverter(MinecraftKey.getConverter()));
public WrappedClientboundUpdateEnabledFeaturesPacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedClientboundUpdateEnabledFeaturesPacket(Set<MinecraftKey> features) {
this(new PacketContainer(TYPE, CONSTRUCTOR.create(features)));
}
public WrappedClientboundUpdateEnabledFeaturesPacket(PacketContainer packet) {
super(packet, TYPE);
}
public Set<MinecraftKey> getFeatures() {
return handle.getSets(MinecraftKey.getConverter()).readSafely(0);
}
public void setFeatures(Set<MinecraftKey> features) {
handle.getSets(MinecraftKey.getConverter()).writeSafely(0, features);
}
}
@@ -1,44 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.clientbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.EquivalentConstructor;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.MinecraftKey;
import com.comphenix.protocol.wrappers.WrappedTagPayload;
import net.dmulloy2.protocol.AbstractPacket;
import java.util.Map;
/**
* Wrapper for {@code ClientboundUpdateTagsPacket} (configuration phase, clientbound).
*/
public class WrappedClientboundUpdateTagsPacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Server.UPDATE_TAGS;
private static final EquivalentConstructor CONSTRUCTOR = new EquivalentConstructor(TYPE)
.withParam(Map.class, BukkitConverters.getMapConverter(
WrappedTagPayload.REGISTRY_KEY_CONVERTER,
WrappedTagPayload.CONVERTER));
public WrappedClientboundUpdateTagsPacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedClientboundUpdateTagsPacket(Map<MinecraftKey, WrappedTagPayload> tags) {
this(new PacketContainer(TYPE, CONSTRUCTOR.create(tags)));
}
public WrappedClientboundUpdateTagsPacket(PacketContainer packet) {
super(packet, TYPE);
}
public Map<MinecraftKey, WrappedTagPayload> getTags() {
return handle.getMaps(WrappedTagPayload.REGISTRY_KEY_CONVERTER, WrappedTagPayload.CONVERTER).read(0);
}
public void setTags(Map<MinecraftKey, WrappedTagPayload> tags) {
handle.getMaps(WrappedTagPayload.REGISTRY_KEY_CONVERTER, WrappedTagPayload.CONVERTER).write(0, tags);
}
}
@@ -1,81 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.serverbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.AutoWrapper;
import com.comphenix.protocol.wrappers.EnumWrappers;
import net.dmulloy2.protocol.AbstractPacket;
/**
* Wrapper for {@code ServerboundClientInformationPacket} (configuration phase, serverbound).
*
* <p>The packet wraps a single {@code ClientInformation} record, exposed via
* {@link #getInformation()} / {@link #setInformation(WrappedClientInformation)}.
*
* <p>NMS: {@code record ClientInformation(String language, int viewDistance,
* ChatVisiblity chatVisibility, boolean chatColors, int modelCustomisation,
* HumanoidArm mainHand, boolean textFilteringEnabled, boolean allowsListing,
* ParticleStatus particleStatus)}
*/
public class WrappedServerboundClientInformationPacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Client.CLIENT_INFORMATION;
private static final Class<?> CLIENT_INFORMATION_CLASS =
MinecraftReflection.getMinecraftClass("server.level.ClientInformation");
private static final Class<?> HUMANOID_ARM_CLASS =
MinecraftReflection.getMinecraftClass("world.entity.HumanoidArm");
private static final Class<?> PARTICLE_STATUS_CLASS =
MinecraftReflection.getMinecraftClass("server.level.ParticleStatus");
private static final EnumWrappers.EnumConverter<MainHand> MAIN_HAND_CONVERTER =
new EnumWrappers.EnumConverter<>(HUMANOID_ARM_CLASS, MainHand.class);
private static final AutoWrapper<WrappedClientInformation> INFORMATION_WRAPPER =
AutoWrapper.wrap(WrappedClientInformation.class, CLIENT_INFORMATION_CLASS)
.field(2, EnumWrappers.getChatVisibilityConverter())
.field(5, MAIN_HAND_CONVERTER)
.field(8, EnumWrappers.getGenericConverter(PARTICLE_STATUS_CLASS, ParticleStatus.class));
public WrappedServerboundClientInformationPacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedServerboundClientInformationPacket(PacketContainer packet) {
super(packet, TYPE);
}
/** Returns the client settings, read from the underlying NMS {@code ClientInformation} record. */
public WrappedClientInformation getInformation() {
return handle.getModifier().withType(CLIENT_INFORMATION_CLASS, INFORMATION_WRAPPER).read(0);
}
/** Writes the client settings back to the underlying NMS {@code ClientInformation} record. */
public void setInformation(WrappedClientInformation info) {
handle.getModifier().withType(CLIENT_INFORMATION_CLASS, INFORMATION_WRAPPER).write(0, info);
}
/** POJO mirroring {@code record ClientInformation(...)}. */
public static final class WrappedClientInformation {
public String language;
public int viewDistance;
/** ProtocolLib {@link EnumWrappers.ChatVisibility}. */
public EnumWrappers.ChatVisibility chatVisibility;
public boolean chatColors;
public int modelCustomisation;
/** Dominant arm: {@link MainHand#LEFT} or {@link MainHand#RIGHT}. */
public MainHand mainHand;
public boolean textFilteringEnabled;
public boolean allowsListing;
public ParticleStatus particleStatus;
public WrappedClientInformation() {}
}
/** Mirrors {@code HumanoidArm}: the player's dominant arm setting. */
public enum MainHand { LEFT, RIGHT }
/** Mirrors NMS {@code ParticleStatus}; constant order must match. */
public enum ParticleStatus { ALL, DECREASED, MINIMAL }
}
@@ -1,54 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.serverbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.EquivalentConstructor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.MinecraftKey;
import net.dmulloy2.protocol.AbstractPacket;
/**
* Wrapper for {@code ServerboundCookieResponsePacket} (configuration phase, serverbound).
*
* <p>Packet structure:
* <ul>
* <li>{@code Identifier key} – namespaced key identifying the cookie</li>
* <li>{@code byte[] payload} – cookie bytes, or {@code null} if the client has no value</li>
* </ul>
*/
public class WrappedServerboundCookieResponsePacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Client.COOKIE_RESPONSE;
private static final EquivalentConstructor CONSTRUCTOR = new EquivalentConstructor(TYPE)
.withParam(MinecraftReflection.getMinecraftKeyClass(), MinecraftKey.getConverter())
.withParam(byte[].class);
public WrappedServerboundCookieResponsePacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedServerboundCookieResponsePacket(MinecraftKey key, byte[] payload) {
this(new PacketContainer(TYPE, CONSTRUCTOR.create(key, payload)));
}
public WrappedServerboundCookieResponsePacket(PacketContainer packet) {
super(packet, TYPE);
}
public MinecraftKey getKey() {
return handle.getMinecraftKeys().readSafely(0);
}
public void setKey(MinecraftKey key) {
handle.getMinecraftKeys().writeSafely(0, key);
}
public byte[] getPayload() {
return handle.getByteArrays().readSafely(0);
}
public void setPayload(byte[] payload) {
handle.getByteArrays().writeSafely(0, payload);
}
}
@@ -1,45 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.serverbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.EquivalentConstructor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.Converters;
import com.comphenix.protocol.wrappers.MinecraftKey;
import com.comphenix.protocol.wrappers.nbt.NbtBase;
import net.dmulloy2.protocol.AbstractPacket;
import java.util.Optional;
/**
* Wrapper for {@code ServerboundCustomClickActionPacket} (configuration phase, serverbound).
*
* <p>Sent when the client clicks a custom button identified by a resource-location key.
*/
public class WrappedServerboundCustomClickActionPacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Client.CUSTOM_CLICK_ACTION;
private static final EquivalentConstructor CONSTRUCTOR = new EquivalentConstructor(TYPE)
.withParam(MinecraftReflection.getMinecraftKeyClass(), MinecraftKey.getConverter())
.withParam(Optional.class, Converters.optional(BukkitConverters.getNbtConverter()));
public WrappedServerboundCustomClickActionPacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedServerboundCustomClickActionPacket(MinecraftKey id, Optional<NbtBase<?>> payload) {
this(new PacketContainer(TYPE, CONSTRUCTOR.create(id, payload)));
}
public WrappedServerboundCustomClickActionPacket(PacketContainer packet) {
super(packet, TYPE);
}
public MinecraftKey getId() {
return handle.getMinecraftKeys().read(0);
}
public void setId(MinecraftKey id) {
handle.getMinecraftKeys().write(0, id);
}
}
@@ -1,42 +0,0 @@
package net.dmulloy2.protocol.wrappers.configuration.serverbound;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.injector.EquivalentConstructor;
import com.comphenix.protocol.wrappers.CustomPacketPayloadWrapper;
import net.dmulloy2.protocol.AbstractPacket;
/**
* Wrapper for {@code ServerboundCustomPayloadPacket} (configuration phase, serverbound).
*
* <p>Packet structure:
* <ul>
* <li>{@code CustomPacketPayload payload} – channel-specific payload data</li>
* </ul>
*/
public class WrappedServerboundCustomPayloadPacket extends AbstractPacket {
public static final PacketType TYPE = PacketType.Configuration.Client.CUSTOM_PAYLOAD;
private static final EquivalentConstructor CONSTRUCTOR = new EquivalentConstructor(TYPE)
.withParam(CustomPacketPayloadWrapper.getCustomPacketPayloadClass(), CustomPacketPayloadWrapper.getConverter());
public WrappedServerboundCustomPayloadPacket() {
super(new PacketContainer(TYPE), TYPE);
}
public WrappedServerboundCustomPayloadPacket(CustomPacketPayloadWrapper payload) {
this(new PacketContainer(TYPE, CONSTRUCTOR.create(payload)));
}
public WrappedServerboundCustomPayloadPacket(PacketContainer packet) {
super(packet, TYPE);
}
public CustomPacketPayloadWrapper getPayload() {
return handle.getCustomPacketPayloads().read(0);
}
public void setPayload(CustomPacketPayloadWrapper payload) {
handle.getCustomPacketPayloads().write(0, payload);
}
}
Loaded 100 of 586 files, more files were not shown because too many files have changed in this diff. Show more