Compare commits

...
55 Commits
Author SHA1 Message Date
Dan Mulloy 4713d320f8 Update version to 4.4.0 for release 2018-09-22 12:21:17 -04:00
Dan Mulloy 083c8e1481 Mark ParticleTypes as immutable, update tested version 2018-09-22 12:17:02 -04:00
Dan Mulloy 6ff6976e85 Merge branch 'master' of https://github.com/aadnk/ProtocolLib 2018-09-18 17:04:10 -04:00
Dan Mulloy 7e87c90b8d Add method to create wrapped block data from the spigot api
Fixes #525
2018-09-18 17:02:19 -04:00
Dan Mulloy 6fdf0f8255 Add modifier for dimensions 2018-09-18 17:01:55 -04:00
Dan Mulloy 7e12f57eda Update testing nms package 2018-09-15 14:48:40 -04:00
Dan Mulloy 776ec56a2d Update to 1.13.1, rework cloning, fix a particle NPE 2018-09-15 14:32:18 -04:00
Dan Mulloy 8ec31d83db Fix #508 2018-08-24 10:44:37 -04:00
diffblue-assistant 1373911ca2 Use try-with-resources (#158)
Replace try statements that close a resource in the finally block with a
try-with-resources statement [1].

This commit was created automatically by Diffblue refactorings (https://www.diffblue.com/).

[1] https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
2018-08-23 12:51:21 -04:00
Dan Mulloy cf5d9dfc7d Account for new network manager send packet method
Fixes #497
2018-08-03 22:18:32 -04:00
Dan Mulloy 9df0dd48e4 Fix backwards compat with nullable classes
Fixes #499
2018-08-03 22:07:25 -04:00
Dan Mulloy d112e9b1dc Remove extraneous debug
#494
2018-08-03 21:58:54 -04:00
Dan Mulloy 206dc30e17 How did those get there 2018-08-01 17:44:25 -04:00
Dan Mulloy 4c3a0afd7e Shiny new 1.13 particle support
Hopefully this doesn't break 1.8-1.12
2018-08-01 17:38:56 -04:00
Dan Mulloy 8d9bf7d91f Clone Java optionals
Fixes #498
2018-08-01 16:08:53 -04:00
Dan Mulloy 3ef537242f Fix some issues with item equality
Seems to be an internal Bukkit bug, hopefully nothing more serious
2018-07-24 17:05:30 -04:00
Dan Mulloy 976e4b4217 Update to 1.13 2018-07-23 19:11:07 -04:00
Dan Mulloy e827a3dd1f Fix class not found exception on versions < 1.13 2018-07-22 00:49:03 -04:00
Dan Mulloy 778031c105 Change block converter to use reflection again 2018-07-19 13:29:20 -04:00
Dan Mulloy 6c3654ef40 Update to 1.13-pre7 2018-07-19 13:13:06 -04:00
Dan Mulloy 5048f6f34b Try a less expensive map impl for packet markers
This might be a concurrency nightmare or a nice solution. I'm hopeful
based on initial testing
Addresses #489
2018-07-09 22:32:21 -04:00
Dan Mulloy c9dd6e2ba7 Merge remote-tracking branch 'origin/master' 2018-07-09 22:31:23 -04:00
Dan Mulloy fc76af9d56 Use broader catch statement when dealing with other metrics plugins
Fixes #488
2018-07-09 22:31:03 -04:00
dextonanderson aed2285bcb Properly cleanup ByteBuf in WirePacket (#487)
* Properly cleanup ByteBuf

* Release store ByteBuf also
2018-07-09 12:07:43 -04:00
Alvin-LB 1d11908af1 Make sure getMinecraftItemStack(ItemStack) handles Material.AIR properly (#485)
Fixes #483 and adds a test case to make sure the issue doesn't occur again.
2018-06-29 12:07:48 -04:00
Dan Mulloy 3ccc56fef9 Update repository links and versions 2018-06-12 14:45:36 -04:00
Dan Mulloy 39e0c974ee Merge branch 'master' of https://github.com/aadnk/ProtocolLib 2018-06-04 12:59:28 -04:00
Dan Mulloy e52c69ca22 Merge pull request #152 from MatrixTunnel/master
Fix maven/gradle repo url
2018-06-04 12:58:52 -04:00
MatrixTunnel d7d53bd905 Fix maven/gradle repo url 2018-06-02 21:26:31 -07:00
Dan Mulloy fb2d5016e5 Fix index out of bounds exception
Also make /all/ the IntelliJ code changes

Fixes #469
2018-05-31 20:15:47 -04:00
Dan Mulloy 015099b92e Clone NBT tags
Fixes #366
2018-05-29 10:53:07 -04:00
Dan Mulloy 7e8356f0bb Remove zip functionality from the updater
I don't upload zip files anyways
2018-05-29 10:41:13 -04:00
Dan Mulloy 72b8c4ced8 👀 2018-05-28 23:21:03 -04:00
Dan Mulloy 2d22df404a Fix Gradle builds 2018-05-28 23:19:56 -04:00
MiniDigger 0f13a32925 add paper to the "isUsingSpigot" check (#468)
partially fixes #467
will make protocollib use the spigot updater on paper servers.
2018-05-18 14:10:46 -04:00
Dan Mulloy 1ddb7a0014 Fix a null pointer with the Bukkit updater 2018-04-14 13:53:54 -04:00
Dan Mulloy f9376432c4 Metrics improvements 2017-10-08 16:02:10 -04:00
Dan Mulloy d195fb6990 Update version constants to 1.12.2 2017-10-08 15:56:13 -04:00
Dan Mulloy e4be102a77 Update to 1.12.2 2017-09-27 19:44:34 -04:00
Dan Mulloy 5f14ccdc34 Add back EnumConverters#getGenericConverter(Class)
It should work, but is deprecated and not recommended
Addresses #394
2017-09-27 19:43:38 -04:00
Mark Hughes 7573459a48 Update build information (#409)
- Switch to 4.3.0 version
- Include Gradle information
2017-09-27 19:19:42 -04:00
Dan Mulloy 01b68cf8e5 Revert unnecessarily breaking changes to converters
Fixes #394
2017-08-30 21:51:21 -04:00
Gabriele C f3b1a07189 Replace MCstats with bStats (#389)
#388
2017-08-19 11:25:54 -04:00
Dan Mulloy c70eb27653 Update ISSUE_TEMPLATE.md 2017-08-19 11:12:21 -04:00
Dan Mulloy 4483ce0d79 Add NbtFactory#fromItemOptional
This new method won't create tags if they don't already exists

Fixes #376
2017-08-13 23:00:51 -04:00
Dan Mulloy 1b23e9ec22 Rewrite the packet metadata API based on the NMS packet
Meta, if created, will persist between creation and listening.
2017-08-13 22:47:45 -04:00
Dan Mulloy 1f5692a0c7 Merge remote-tracking branch 'origin/master' 2017-08-04 14:01:24 -04:00
Dan Mulloy 0ac68dc179 Update to 1.12.1
Also contains changes for the packet wrapper update coming soonTM
2017-08-04 14:01:06 -04:00
dadus33 d891a057c0 Make #deserializeLegacy() able to access private members (#386) 2017-07-30 22:31:18 -04:00
Dan Mulloy 9e5bdf4124 Major rework of underlying conversion logic
This shouldn't affect plugins utlilizing the API, but will make updates and adding wrappers to PacketWrapper easier. In case you're wondering, major changes include:

- Added AutoWrapper
- Added Covnerters utility class
- Removed generic type parameter from EquivalentConverter
2017-07-24 14:15:56 -04:00
Dan Mulloy e1ea295600 Fix one of many issues with reloading
I'm only fixing this because it was relatively simple and I got a lot of reports for it. Seriously people, Stop. Reloading.

Fixes #373, fixes #368, fixes #360, fixes #359
2017-07-08 02:10:24 -04:00
蒋天蓓 59171c5443 Fix issue with component array converter (#372) 2017-07-07 11:42:17 -04:00
Dan Mulloy 501244cb3c Attempt to fix Jenkins build
Oddly enough it builds with no errors on my computer
2017-07-06 14:32:35 -04:00
Dan Mulloy 390c21f6d5 Add PacketEvent#isPlayerTemporary, check for player updates
This should address issues with temporary players by hopefully returning them less often
2017-07-06 13:56:30 -04:00
Dan Mulloy 20d78832b0 Update dependency versions to hopefully work with Java 9 2017-06-14 15:41:00 -04:00
89 changed files with 6243 additions and 5495 deletions

No files matched your search

+2 -1
View File
@@ -4,7 +4,8 @@ Make sure you've done the following:
- [ ] You're using the latest build for your server version
- [ ] This isn't an issue caused by another plugin
- [ ] You've checked for duplicate issues
- [ ] You didn't use `/reload`
Debug paste link:
Description and relevant errors:
Description and relevant errors:
+15 -9
View File
@@ -44,7 +44,7 @@ You can also add ProtocolLib as a Maven dependency:
<repositories>
<repository>
<id>dmulloy2-repo</id>
<url>http://repo.dmulloy2.net/content/groups/public/</url>
<url>http://repo.dmulloy2.net/nexus/repository/public/</url>
</repository>
...
</repositories>
@@ -52,12 +52,24 @@ You can also add ProtocolLib as a Maven dependency:
<dependencies>
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>3.6.5</version>
<artifactId>ProtocolLib-API</artifactId>
<version>4.4.0</version>
</dependency>
</dependencies>
````
Or use the maven dependency with gradle:
```gradle
repositories {
maven { url "http://repo.dmulloy2.net/nexus/repository/public/" }
}
dependencies {
compileOnly group: "com.comphenix.protocol", name: "ProtocolLib", version: "4.4.0";
}
```
Then get a reference to ProtocolManager in onLoad() or onEnable() and you're good to go.
````java
@@ -155,9 +167,3 @@ It's likely that I won't have to update ProtocolLib for anything but bug fixes a
How is this possible? It all comes down to reflection in the end. Essentially, no name is hard coded -
every field, method and class is deduced by looking at field types, package names or parameter
types. It's remarkably consistent across different versions.
### Incompatibility
The following plugins (to be expanded) are not compatible with ProtocolLib:
* TagAPI
+5 -7
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>ProtocolLib-API</artifactId>
<name>ProtocolLib-API</name>
<version>${minorVersion}</version>
<version>4.4.0</version>
<description>Provides read/write access to the Minecraft protocol.</description>
<url>http://www.spigotmc.org/resources/protocollib.1997/</url>
@@ -12,7 +12,7 @@
<parent>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib-Parent</artifactId>
<version>v4-SNAPSHOT</version>
<version>4.4.0-SNAPSHOT</version>
<relativePath>../../</relativePath>
</parent>
@@ -20,13 +20,11 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.number></project.build.number>
<project.fullVersion>${project.version}</project.fullVersion>
<spigotVersion>1.12-pre2-SNAPSHOT</spigotVersion>
</properties>
<build>
<defaultGoal>clean install</defaultGoal>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
@@ -40,7 +38,7 @@
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
@@ -50,7 +48,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<version>3.1.0</version>
<configuration>
<archive>
<manifestEntries>
@@ -67,7 +65,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.10</version>
<version>2.22.0</version>
<configuration>
<systemProperties>
<property>
@@ -109,85 +109,92 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static class Server extends PacketTypeEnum {
private final static Sender SENDER = Sender.SERVER;
public static final PacketType SPAWN_ENTITY = new PacketType(PROTOCOL, SENDER, 0x00, 0x0E, "SpawnEntity");
public static final PacketType SPAWN_ENTITY_EXPERIENCE_ORB = new PacketType(PROTOCOL, SENDER, 0x01, 0x11, "SpawnEntityExperienceOrb");
public static final PacketType SPAWN_ENTITY_WEATHER = new PacketType(PROTOCOL, SENDER, 0x02, 0x2C, "SpawnEntityWeather");
public static final PacketType SPAWN_ENTITY_LIVING = new PacketType(PROTOCOL, SENDER, 0x03, 0x0F, "SpawnEntityLiving");
public static final PacketType SPAWN_ENTITY_PAINTING = new PacketType(PROTOCOL, SENDER, 0x04, 0x10, "SpawnEntityPainting");
public static final PacketType NAMED_ENTITY_SPAWN = new PacketType(PROTOCOL, SENDER, 0x05, 0x0C, "NamedEntitySpawn");
public static final PacketType ANIMATION = new PacketType(PROTOCOL, SENDER, 0x06, 0x0B, "Animation");
public static final PacketType STATISTIC = new PacketType(PROTOCOL, SENDER, 0x07, 0x37, "Statistic");
public static final PacketType BLOCK_BREAK_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x08, 0x25, "BlockBreakAnimation");
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x09, 0x35, "TileEntityData");
public static final PacketType BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x0A, 0x24, "BlockAction");
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x0B, 0x23, "BlockChange");
public static final PacketType BOSS = new PacketType(PROTOCOL, SENDER, 0x0C, 0xFF, "Boss");
public static final PacketType SERVER_DIFFICULTY = new PacketType(PROTOCOL, SENDER, 0x0D, 0x41, "ServerDifficulty");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x0E, 0x3A, "TabComplete");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x0F, 0x02, "Chat");
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x10, 0x22, "MultiBlockChange");
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x11, 0x32, "Transaction");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x12, 0x2E, "CloseWindow");
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x13, 0x2D, "OpenWindow");
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x14, 0x30, "WindowItems");
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x15, 0x31, "WindowData");
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x16, 0x2F, "SetSlot");
public static final PacketType SET_COOLDOWN = new PacketType(PROTOCOL, SENDER, 0x17, 0xFC, "SetCooldown");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x18, 0x3F, "CustomPayload");
public static final PacketType CUSTOM_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x19, 0xFD, "CustomSoundEffect");
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x1A, 0x40, "KickDisconnect");
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x1B, 0x1A, "EntityStatus");
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x1C, 0x27, "Explosion");
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x1D, 0xFA, "UnloadChunk");
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x1E, 0x2B, "GameStateChange");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x1F, 0x00, "KeepAlive");
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x20, 0x21, "MapChunk");
public static final PacketType WORLD_EVENT = new PacketType(PROTOCOL, SENDER, 0x21, 0x28, "WorldEvent");
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x22, 0x2A, "WorldParticles");
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x23, 0x01, "Login");
public static final PacketType MAP = new PacketType(PROTOCOL, SENDER, 0x24, 0x34, "Map");
public static final PacketType ENTITY = new PacketType(PROTOCOL, SENDER, 0x25, 0x14, "Entity");
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x26, 0x15, "RelEntityMove");
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x27, 0x17, "RelEntityMoveLook");
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x28, 0x16, "EntityLook");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x29, 0xFE, "VehicleMove");
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x2A, 0x36, "OpenSignEditor");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x2B, 0x39, "Abilities");
public static final PacketType COMBAT_EVENT = new PacketType(PROTOCOL, SENDER, 0x2C, 0x42, "CombatEvent");
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x2D, 0x38, "PlayerInfo");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x2E, 0x08, "Position");
public static final PacketType BED = new PacketType(PROTOCOL, SENDER, 0x2F, 0x0A, "Bed");
public static final PacketType RECIPES = new PacketType(PROTOCOL, SENDER, 0x30, -1, "Recipes");
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x31, 0x13, "EntityDestroy");
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x32, 0x1E, "RemoveEntityEffect");
public static final PacketType RESOURCE_PACK_SEND = new PacketType(PROTOCOL, SENDER, 0x33, 0x48, "ResourcePackSend");
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x34, 0x07, "Respawn");
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x35, 0x19, "EntityHeadRotation");
public static final PacketType SELECT_ADVANCEMENT_TAB = new PacketType(PROTOCOL, SENDER, 0x36, -1, "SelectAdvancementTab");
public static final PacketType WORLD_BORDER = new PacketType(PROTOCOL, SENDER, 0x37, 0x44, "WorldBorder");
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x38, 0x43, "Camera");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x39, 0x09, "HeldItemSlot");
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x3A, 0x3D, "ScoreboardDisplayObjective");
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x3B, 0x1C, "EntityMetadata");
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x3C, 0x1B, "AttachEntity");
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x3D, 0x12, "EntityVelocity");
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x3E, 0x04, "EntityEquipment");
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x3F, 0x1F, "Experience");
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x40, 0x06, "UpdateHealth");
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x41, 0x3B, "ScoreboardObjective");
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x42, 0xFB, "Mount");
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x43, 0x3E, "ScoreboardTeam");
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x44, 0x3C, "ScoreboardScore");
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x45, 0x05, "SpawnPosition");
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x46, 0x03, "UpdateTime");
public static final PacketType TITLE = new PacketType(PROTOCOL, SENDER, 0x47, 0x45, "Title");
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x48, 0x29, "NamedSoundEffect");
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x49, 0x47, "PlayerListHeaderFooter");
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x4A, 0x0D, "Collect");
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x4B, 0x18, "EntityTeleport");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x4C, -1, "Advancements");
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x4D, 0x20, "UpdateAttributes");
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x4E, 0x1D, "EntityEffect");
public static final PacketType SPAWN_ENTITY = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "SpawnEntity");
public static final PacketType SPAWN_ENTITY_EXPERIENCE_ORB = new PacketType(PROTOCOL, SENDER, 0x01, 0x01, "SpawnEntityExperienceOrb");
public static final PacketType SPAWN_ENTITY_WEATHER = new PacketType(PROTOCOL, SENDER, 0x02, 0x02, "SpawnEntityWeather");
public static final PacketType SPAWN_ENTITY_LIVING = new PacketType(PROTOCOL, SENDER, 0x03, 0x03, "SpawnEntityLiving");
public static final PacketType SPAWN_ENTITY_PAINTING = new PacketType(PROTOCOL, SENDER, 0x04, 0x04, "SpawnEntityPainting");
public static final PacketType NAMED_ENTITY_SPAWN = new PacketType(PROTOCOL, SENDER, 0x05, 0x05, "NamedEntitySpawn");
public static final PacketType ANIMATION = new PacketType(PROTOCOL, SENDER, 0x06, 0x06, "Animation");
public static final PacketType STATISTIC = new PacketType(PROTOCOL, SENDER, 0x07, 0x07, "Statistic");
public static final PacketType BLOCK_BREAK_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x08, 0x08, "BlockBreakAnimation");
public static final PacketType TILE_ENTITY_DATA = new PacketType(PROTOCOL, SENDER, 0x09, 0x09, "TileEntityData");
public static final PacketType BLOCK_ACTION = new PacketType(PROTOCOL, SENDER, 0x0A, 0x0A, "BlockAction");
public static final PacketType BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x0B, 0x0B, "BlockChange");
public static final PacketType BOSS = new PacketType(PROTOCOL, SENDER, 0x0C, 0x0C, "Boss");
public static final PacketType SERVER_DIFFICULTY = new PacketType(PROTOCOL, SENDER, 0x0D, 0x0D, "ServerDifficulty");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x0E, 0x0F, "Chat");
public static final PacketType MULTI_BLOCK_CHANGE = new PacketType(PROTOCOL, SENDER, 0x0F, 0x10, "MultiBlockChange");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x10, 0x0E, "TabComplete");
public static final PacketType COMMANDS = new PacketType(PROTOCOL, SENDER, 0x11, -0x01, "Commands");
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x12, 0x11, "Transaction");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x13, 0x12, "CloseWindow");
public static final PacketType OPEN_WINDOW = new PacketType(PROTOCOL, SENDER, 0x14, 0x13, "OpenWindow");
public static final PacketType WINDOW_ITEMS = new PacketType(PROTOCOL, SENDER, 0x15, 0x14, "WindowItems");
public static final PacketType WINDOW_DATA = new PacketType(PROTOCOL, SENDER, 0x16, 0x15, "WindowData");
public static final PacketType SET_SLOT = new PacketType(PROTOCOL, SENDER, 0x17, 0x16, "SetSlot");
public static final PacketType SET_COOLDOWN = new PacketType(PROTOCOL, SENDER, 0x18, 0x17, "SetCooldown");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x19, 0x18, "CustomPayload");
public static final PacketType CUSTOM_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x1A, 0x19, "CustomSoundEffect");
public static final PacketType KICK_DISCONNECT = new PacketType(PROTOCOL, SENDER, 0x1B, 0x1A, "KickDisconnect");
public static final PacketType ENTITY_STATUS = new PacketType(PROTOCOL, SENDER, 0x1C, 0x1B, "EntityStatus");
public static final PacketType NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x1D, -0x01, "NBTQuery");
public static final PacketType EXPLOSION = new PacketType(PROTOCOL, SENDER, 0x1E, 0x1C, "Explosion");
public static final PacketType UNLOAD_CHUNK = new PacketType(PROTOCOL, SENDER, 0x1F, 0x1D, "UnloadChunk");
public static final PacketType GAME_STATE_CHANGE = new PacketType(PROTOCOL, SENDER, 0x20, 0x1E, "GameStateChange");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x21, 0x1F, "KeepAlive");
public static final PacketType MAP_CHUNK = new PacketType(PROTOCOL, SENDER, 0x22, 0x20, "MapChunk");
public static final PacketType WORLD_EVENT = new PacketType(PROTOCOL, SENDER, 0x23, 0x21, "WorldEvent");
public static final PacketType WORLD_PARTICLES = new PacketType(PROTOCOL, SENDER, 0x24, 0x22, "WorldParticles");
public static final PacketType LOGIN = new PacketType(PROTOCOL, SENDER, 0x25, 0x23, "Login");
public static final PacketType MAP = new PacketType(PROTOCOL, SENDER, 0x26, 0x24, "Map");
public static final PacketType ENTITY = new PacketType(PROTOCOL, SENDER, 0x27, 0x25, "Entity");
public static final PacketType REL_ENTITY_MOVE = new PacketType(PROTOCOL, SENDER, 0x28, 0x26, "RelEntityMove");
public static final PacketType REL_ENTITY_MOVE_LOOK = new PacketType(PROTOCOL, SENDER, 0x29, 0x27, "RelEntityMoveLook");
public static final PacketType ENTITY_LOOK = new PacketType(PROTOCOL, SENDER, 0x2A, 0x28, "EntityLook");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x2B, 0x29, "VehicleMove");
public static final PacketType OPEN_SIGN_EDITOR = new PacketType(PROTOCOL, SENDER, 0x2C, 0x2A, "OpenSignEditor");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x2D, 0x2B, "AutoRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x2E, 0x2C, "Abilities");
public static final PacketType COMBAT_EVENT = new PacketType(PROTOCOL, SENDER, 0x2F, 0x2D, "CombatEvent");
public static final PacketType PLAYER_INFO = new PacketType(PROTOCOL, SENDER, 0x30, 0x2E, "PlayerInfo");
public static final PacketType LOOK_AT = new PacketType(PROTOCOL, SENDER, 0x31, -0x01, "LookAt");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x32, 0x2F, "Position");
public static final PacketType BED = new PacketType(PROTOCOL, SENDER, 0x33, 0x30, "Bed");
public static final PacketType RECIPES = new PacketType(PROTOCOL, SENDER, 0x34, 0x31, "Recipes");
public static final PacketType ENTITY_DESTROY = new PacketType(PROTOCOL, SENDER, 0x35, 0x32, "EntityDestroy");
public static final PacketType REMOVE_ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x36, 0x33, "RemoveEntityEffect");
public static final PacketType RESOURCE_PACK_SEND = new PacketType(PROTOCOL, SENDER, 0x37, 0x34, "ResourcePackSend");
public static final PacketType RESPAWN = new PacketType(PROTOCOL, SENDER, 0x38, 0x35, "Respawn");
public static final PacketType ENTITY_HEAD_ROTATION = new PacketType(PROTOCOL, SENDER, 0x39, 0x36, "EntityHeadRotation");
public static final PacketType SELECT_ADVANCEMENT_TAB = new PacketType(PROTOCOL, SENDER, 0x3A, 0x37, "SelectAdvancementTab");
public static final PacketType WORLD_BORDER = new PacketType(PROTOCOL, SENDER, 0x3B, 0x38, "WorldBorder");
public static final PacketType CAMERA = new PacketType(PROTOCOL, SENDER, 0x3C, 0x39, "Camera");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x3D, 0x3A, "HeldItemSlot");
public static final PacketType SCOREBOARD_DISPLAY_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x3E, 0x3B, "ScoreboardDisplayObjective");
public static final PacketType ENTITY_METADATA = new PacketType(PROTOCOL, SENDER, 0x3F, 0x3C, "EntityMetadata");
public static final PacketType ATTACH_ENTITY = new PacketType(PROTOCOL, SENDER, 0x40, 0x3D, "AttachEntity");
public static final PacketType ENTITY_VELOCITY = new PacketType(PROTOCOL, SENDER, 0x41, 0x3E, "EntityVelocity");
public static final PacketType ENTITY_EQUIPMENT = new PacketType(PROTOCOL, SENDER, 0x42, 0x3F, "EntityEquipment");
public static final PacketType EXPERIENCE = new PacketType(PROTOCOL, SENDER, 0x43, 0x40, "Experience");
public static final PacketType UPDATE_HEALTH = new PacketType(PROTOCOL, SENDER, 0x44, 0x41, "UpdateHealth");
public static final PacketType SCOREBOARD_OBJECTIVE = new PacketType(PROTOCOL, SENDER, 0x45, 0x42, "ScoreboardObjective");
public static final PacketType MOUNT = new PacketType(PROTOCOL, SENDER, 0x46, 0x43, "Mount");
public static final PacketType SCOREBOARD_TEAM = new PacketType(PROTOCOL, SENDER, 0x47, 0x44, "ScoreboardTeam");
public static final PacketType SCOREBOARD_SCORE = new PacketType(PROTOCOL, SENDER, 0x48, 0x45, "ScoreboardScore");
public static final PacketType SPAWN_POSITION = new PacketType(PROTOCOL, SENDER, 0x49, 0x46, "SpawnPosition");
public static final PacketType UPDATE_TIME = new PacketType(PROTOCOL, SENDER, 0x4A, 0x47, "UpdateTime");
public static final PacketType TITLE = new PacketType(PROTOCOL, SENDER, 0x4B, 0x48, "Title");
public static final PacketType STOP_SOUND = new PacketType(PROTOCOL, SENDER, 0x4C, -0x01, "StopSound");
public static final PacketType NAMED_SOUND_EFFECT = new PacketType(PROTOCOL, SENDER, 0x4D, 0x49, "NamedSoundEffect");
public static final PacketType PLAYER_LIST_HEADER_FOOTER = new PacketType(PROTOCOL, SENDER, 0x4E, 0x4A, "PlayerListHeaderFooter");
public static final PacketType COLLECT = new PacketType(PROTOCOL, SENDER, 0x4F, 0x4B, "Collect");
public static final PacketType ENTITY_TELEPORT = new PacketType(PROTOCOL, SENDER, 0x50, 0x4C, "EntityTeleport");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x51, 0x4D, "Advancements");
public static final PacketType UPDATE_ATTRIBUTES = new PacketType(PROTOCOL, SENDER, 0x52, 0x4E, "UpdateAttributes");
public static final PacketType ENTITY_EFFECT = new PacketType(PROTOCOL, SENDER, 0x53, 0x4F, "EntityEffect");
public static final PacketType RECIPE_UPDATE = new PacketType(PROTOCOL, SENDER, 0x54, 0x54, "RecipeUpdate");
public static final PacketType TAGS = new PacketType(PROTOCOL, SENDER, 0x55, 0x55, "Tags");
// ---- Removed in 1.9
@@ -264,39 +271,49 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static class Client extends PacketTypeEnum {
private final static Sender SENDER = Sender.CLIENT;
public static final PacketType TELEPORT_ACCEPT = new PacketType(PROTOCOL, SENDER, 0x00, 0xF9, "TeleportAccept");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x01, -1, "AutoRecipe");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x02, 0x14, "TabComplete");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x03, 0x01, "Chat");
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x04, 0x16, "ClientCommand");
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x05, 0x15, "Settings");
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x06, 0x0F, "Transaction");
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x07, 0x11, "EnchantItem");
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x08, 0x0E, "WindowClick");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x09, 0x0D, "CloseWindow");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x0A, 0x17, "CustomPayload");
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x0B, 0x02, "UseEntity");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x0C, 0x00, "KeepAlive");
public static final PacketType FLYING = new PacketType(PROTOCOL, SENDER, 0x0D, 0x03, "Flying");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x0E, 0x04, "Position");
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x0F, 0x06, "PositionLook");
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x10, 0x05, "Look");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x11, 0xF8, "VehicleMove");
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x12, 0xF7, "BoatMove");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x13, 0x13, "Abilities");
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x14, 0x07, "BlockDig");
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x15, 0x0B, "EntityAction");
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x16, 0x0C, "SteerVehicle");
public static final PacketType RECIPE_DISPLAYED = new PacketType(PROTOCOL, SENDER, 0x17, -1, "RecipeDisplayed");
public static final PacketType RESOURCE_PACK_STATUS = new PacketType(PROTOCOL, SENDER, 0x18, 0x19, "ResourcePackStatus");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x19, -1, "Advancements");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x1A, 0x09, "HeldItemSlot");
public static final PacketType SET_CREATIVE_SLOT = new PacketType(PROTOCOL, SENDER, 0x1B, 0x10, "SetCreativeSlot");
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x1C, 0x12, "UpdateSign");
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x1D, 0x0A, "ArmAnimation");
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x1E, 0x18, "Spectate");
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x1F, 0xF6, "UseItem");
public static final PacketType BLOCK_PLACE = new PacketType(PROTOCOL, SENDER, 0x20, 0x08, "BlockPlace");
public static final PacketType TELEPORT_ACCEPT = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "TeleportAccept");
public static final PacketType TILE_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x01, -0x01, "TileNBTQuery");
public static final PacketType CHAT = new PacketType(PROTOCOL, SENDER, 0x02, 0x02, "Chat");
public static final PacketType CLIENT_COMMAND = new PacketType(PROTOCOL, SENDER, 0x03, 0x03, "ClientCommand");
public static final PacketType SETTINGS = new PacketType(PROTOCOL, SENDER, 0x04, 0x04, "Settings");
public static final PacketType TAB_COMPLETE = new PacketType(PROTOCOL, SENDER, 0x05, 0x01, "TabComplete");
public static final PacketType TRANSACTION = new PacketType(PROTOCOL, SENDER, 0x06, 0x05, "Transaction");
public static final PacketType ENCHANT_ITEM = new PacketType(PROTOCOL, SENDER, 0x07, 0x06, "EnchantItem");
public static final PacketType WINDOW_CLICK = new PacketType(PROTOCOL, SENDER, 0x08, 0x07, "WindowClick");
public static final PacketType CLOSE_WINDOW = new PacketType(PROTOCOL, SENDER, 0x09, 0x08, "CloseWindow");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x0A, 0x09, "CustomPayload");
public static final PacketType BOOK_EDIT = new PacketType(PROTOCOL, SENDER, 0x0B, -0x01, "BEdit");
public static final PacketType ENTITY_NBT_QUERY = new PacketType(PROTOCOL, SENDER, 0x0C, -0x01, "EntityNBTQuery");
public static final PacketType USE_ENTITY = new PacketType(PROTOCOL, SENDER, 0x0D, 0x0A, "UseEntity");
public static final PacketType KEEP_ALIVE = new PacketType(PROTOCOL, SENDER, 0x0E, 0x0B, "KeepAlive");
public static final PacketType FLYING = new PacketType(PROTOCOL, SENDER, 0x0F, 0x0C, "Flying");
public static final PacketType POSITION = new PacketType(PROTOCOL, SENDER, 0x10, 0x0D, "Position");
public static final PacketType POSITION_LOOK = new PacketType(PROTOCOL, SENDER, 0x11, 0x0E, "PositionLook");
public static final PacketType LOOK = new PacketType(PROTOCOL, SENDER, 0x12, 0x0F, "Look");
public static final PacketType VEHICLE_MOVE = new PacketType(PROTOCOL, SENDER, 0x13, 0x10, "VehicleMove");
public static final PacketType BOAT_MOVE = new PacketType(PROTOCOL, SENDER, 0x14, 0x11, "BoatMove");
public static final PacketType PICK_ITEM = new PacketType(PROTOCOL, SENDER, 0x15, -0x01, "PickItem");
public static final PacketType AUTO_RECIPE = new PacketType(PROTOCOL, SENDER, 0x16, 0x12, "AutoRecipe");
public static final PacketType ABILITIES = new PacketType(PROTOCOL, SENDER, 0x17, 0x13, "Abilities");
public static final PacketType BLOCK_DIG = new PacketType(PROTOCOL, SENDER, 0x18, 0x14, "BlockDig");
public static final PacketType ENTITY_ACTION = new PacketType(PROTOCOL, SENDER, 0x19, 0x15, "EntityAction");
public static final PacketType STEER_VEHICLE = new PacketType(PROTOCOL, SENDER, 0x1A, 0x16, "SteerVehicle");
public static final PacketType RECIPE_DISPLAYED = new PacketType(PROTOCOL, SENDER, 0x1B, 0x17, "RecipeDisplayed");
public static final PacketType ITEM_NAME = new PacketType(PROTOCOL, SENDER, 0x1C, -0x01, "ItemName");
public static final PacketType RESOURCE_PACK_STATUS = new PacketType(PROTOCOL, SENDER, 0x1D, 0x18, "ResourcePackStatus");
public static final PacketType ADVANCEMENTS = new PacketType(PROTOCOL, SENDER, 0x1E, 0x19, "Advancements");
public static final PacketType TRADE_SELECT = new PacketType(PROTOCOL, SENDER, 0x1F, -0x01, "TrSel");
public static final PacketType BEACON = new PacketType(PROTOCOL, SENDER, 0x20, -0x01, "Beacon");
public static final PacketType HELD_ITEM_SLOT = new PacketType(PROTOCOL, SENDER, 0x21, 0x1A, "HeldItemSlot");
public static final PacketType SET_COMMAND_BLOCK = new PacketType(PROTOCOL, SENDER, 0x22, 0x22, "SetCommandBlock");
public static final PacketType SET_COMMAND_MINECART = new PacketType(PROTOCOL, SENDER, 0x23, 0x23, "SetCommandMinecart");
public static final PacketType SET_CREATIVE_SLOT = new PacketType(PROTOCOL, SENDER, 0x24, 0x1B, "SetCreativeSlot");
public static final PacketType STRUCTURE_BLOCK = new PacketType(PROTOCOL, SENDER, 0x25, 0x25, "Struct");
public static final PacketType UPDATE_SIGN = new PacketType(PROTOCOL, SENDER, 0x26, 0x1C, "UpdateSign");
public static final PacketType ARM_ANIMATION = new PacketType(PROTOCOL, SENDER, 0x27, 0x1D, "ArmAnimation");
public static final PacketType SPECTATE = new PacketType(PROTOCOL, SENDER, 0x28, 0x1E, "Spectate");
public static final PacketType USE_ITEM = new PacketType(PROTOCOL, SENDER, 0x29, 0x1F, "UseItem");
public static final PacketType BLOCK_PLACE = new PacketType(PROTOCOL, SENDER, 0x2A, 0x20, "BlockPlace");
private final static Client INSTANCE = new Client();
@@ -400,6 +417,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType ENCRYPTION_BEGIN = new PacketType(PROTOCOL, SENDER, 0x01, 0x01, "EncryptionBegin");
public static final PacketType SUCCESS = new PacketType(PROTOCOL, SENDER, 0x02, 0x02, "Success");
public static final PacketType SET_COMPRESSION = new PacketType(PROTOCOL, SENDER, 0x03, 0x03, "SetCompression");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x04, -0x01, "CustomPayload");
private final static Server INSTANCE = new Server();
@@ -423,6 +441,7 @@ public class PacketType implements Serializable, Cloneable, Comparable<PacketTyp
public static final PacketType START = new PacketType(PROTOCOL, SENDER, 0x00, 0x00, "Start");
public static final PacketType ENCRYPTION_BEGIN = new PacketType(PROTOCOL, SENDER, 0x01, 0x01, "EncryptionBegin");
public static final PacketType CUSTOM_PAYLOAD = new PacketType(PROTOCOL, SENDER, 0x02, -0x01, "CustomPayload");
private final static Client INSTANCE = new Client();
@@ -39,12 +39,12 @@ public class ProtocolLibrary {
/**
* The maximum version ProtocolLib has been tested with.
*/
public static final String MAXIMUM_MINECRAFT_VERSION = "1.12";
public static final String MAXIMUM_MINECRAFT_VERSION = "1.13.1";
/**
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.12) was released.
* The date (with ISO 8601 or YYYY-MM-DD) when the most recent version (1.13.1) was released.
*/
public static final String MINECRAFT_LAST_RELEASE_DATE = "2017-06-07";
public static final String MINECRAFT_LAST_RELEASE_DATE = "2018-08-22";
/**
* Plugins that are currently incompatible with ProtocolLib.
@@ -31,11 +31,11 @@ public class ProtocolLogger {
ProtocolLogger.plugin = plugin;
}
private static boolean isDebugEnabled() {
public static boolean isDebugEnabled() {
try {
return plugin.getConfig().getBoolean("global.debug", false);
} catch (Throwable ex) { // Maybe we're testing or something
return false;
} catch (Throwable ex) { // Enable in testing environments
return true;
}
}
@@ -17,37 +17,15 @@
package com.comphenix.protocol.events;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.*;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.WorldType;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.injector.StructureCache;
@@ -55,55 +33,15 @@ import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.ObjectWriter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.cloning.AggregateCloner;
import com.comphenix.protocol.reflect.cloning.*;
import com.comphenix.protocol.reflect.cloning.AggregateCloner.BuilderParameters;
import com.comphenix.protocol.reflect.cloning.BukkitCloner;
import com.comphenix.protocol.reflect.cloning.Cloner;
import com.comphenix.protocol.reflect.cloning.CollectionCloner;
import com.comphenix.protocol.reflect.cloning.FieldCloner;
import com.comphenix.protocol.reflect.cloning.ImmutableDetector;
import com.comphenix.protocol.reflect.cloning.OptionalCloner;
import com.comphenix.protocol.reflect.cloning.SerializableCloner;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.reflect.instances.DefaultInstances;
import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.StreamSerializer;
import com.comphenix.protocol.wrappers.BlockPosition;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.ChunkCoordIntPair;
import com.comphenix.protocol.wrappers.ChunkPosition;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.EnumWrappers.ChatType;
import com.comphenix.protocol.wrappers.EnumWrappers.ChatVisibility;
import com.comphenix.protocol.wrappers.EnumWrappers.ClientCommand;
import com.comphenix.protocol.wrappers.EnumWrappers.CombatEventType;
import com.comphenix.protocol.wrappers.EnumWrappers.Difficulty;
import com.comphenix.protocol.wrappers.EnumWrappers.Direction;
import com.comphenix.protocol.wrappers.EnumWrappers.EntityUseAction;
import com.comphenix.protocol.wrappers.EnumWrappers.EnumConverter;
import com.comphenix.protocol.wrappers.EnumWrappers.Hand;
import com.comphenix.protocol.wrappers.EnumWrappers.ItemSlot;
import com.comphenix.protocol.wrappers.EnumWrappers.NativeGameMode;
import com.comphenix.protocol.wrappers.EnumWrappers.Particle;
import com.comphenix.protocol.wrappers.EnumWrappers.PlayerAction;
import com.comphenix.protocol.wrappers.EnumWrappers.PlayerDigType;
import com.comphenix.protocol.wrappers.EnumWrappers.PlayerInfoAction;
import com.comphenix.protocol.wrappers.EnumWrappers.ResourcePackStatus;
import com.comphenix.protocol.wrappers.EnumWrappers.ScoreboardAction;
import com.comphenix.protocol.wrappers.EnumWrappers.SoundCategory;
import com.comphenix.protocol.wrappers.EnumWrappers.TitleAction;
import com.comphenix.protocol.wrappers.EnumWrappers.WorldBorderAction;
import com.comphenix.protocol.wrappers.MultiBlockChangeInfo;
import com.comphenix.protocol.wrappers.PlayerInfoData;
import com.comphenix.protocol.wrappers.WrappedAttribute;
import com.comphenix.protocol.wrappers.WrappedBlockData;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedServerPing;
import com.comphenix.protocol.wrappers.WrappedStatistic;
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
import com.comphenix.protocol.wrappers.*;
import com.comphenix.protocol.wrappers.EnumWrappers.*;
import com.comphenix.protocol.wrappers.nbt.NbtBase;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
@@ -115,6 +53,15 @@ import com.google.common.collect.Sets;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.UnpooledByteBufAllocator;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.WorldType;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
/**
* Represents a Minecraft packet indirectly.
*
@@ -134,30 +81,30 @@ public class PacketContainer implements Serializable {
private static ConcurrentMap<Class<?>, Method> readMethods = Maps.newConcurrentMap();
// Used to clone packets
private static final AggregateCloner DEEP_CLONER = AggregateCloner.newBuilder().
instanceProvider(DefaultInstances.DEFAULT).
andThen(BukkitCloner.class).
andThen(ImmutableDetector.class).
andThen(OptionalCloner.class).
andThen(CollectionCloner.class).
andThen(getSpecializedDeepClonerFactory()).
build();
private static final AggregateCloner DEEP_CLONER = AggregateCloner
.newBuilder()
.instanceProvider(DefaultInstances.DEFAULT)
.andThen(BukkitCloner.class)
.andThen(ImmutableDetector.class)
.andThen(JavaOptionalCloner.class)
.andThen(GuavaOptionalCloner.class)
.andThen(CollectionCloner.class)
.andThen(getSpecializedDeepClonerFactory())
.build();
private static final AggregateCloner SHALLOW_CLONER = AggregateCloner.newBuilder().
instanceProvider(DefaultInstances.DEFAULT).
andThen(new Function<BuilderParameters, Cloner>() {
@Override
public Cloner apply(@Nullable BuilderParameters param) {
if (param == null)
throw new IllegalArgumentException("Cannot be NULL.");
return new FieldCloner(param.getAggregateCloner(), param.getInstanceProvider()) {{
// Use a default writer with no concept of cloning
writer = new ObjectWriter();
}};
}
}).
build();
private static final AggregateCloner SHALLOW_CLONER = AggregateCloner
.newBuilder()
.instanceProvider(DefaultInstances.DEFAULT)
.andThen(param -> {
if (param == null)
throw new IllegalArgumentException("Cannot be NULL.");
return new FieldCloner(param.getAggregateCloner(), param.getInstanceProvider()) {{
// Use a default writer with no concept of cloning
writer = new ObjectWriter();
}};
})
.build();
// Packets that cannot be cloned by our default deep cloner
private static final Set<PacketType> CLONING_UNSUPPORTED = Sets.newHashSet(
@@ -385,8 +332,9 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<ItemStack> getItemModifier() {
// Convert to and from the Bukkit wrapper
return structureModifier.<ItemStack>withType(
MinecraftReflection.getItemStackClass(), BukkitConverters.getItemStackConverter());
return structureModifier.withType(
MinecraftReflection.getItemStackClass(),
BukkitConverters.getItemStackConverter());
}
/**
@@ -398,9 +346,9 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<ItemStack[]> getItemArrayModifier() {
// Convert to and from the Bukkit wrapper
return structureModifier.<ItemStack[]>withType(
return structureModifier.withType(
MinecraftReflection.getItemStackArrayClass(),
BukkitConverters.getIgnoreNull(new ItemStackArrayConverter()));
Converters.ignoreNull(new ItemStackArrayConverter()));
}
/**
@@ -414,10 +362,7 @@ public class PacketContainer implements Serializable {
// Convert to and from the Bukkit wrapper
return structureModifier.withType(
List.class,
BukkitConverters.getListConverter(
MinecraftReflection.getItemStackClass(),
BukkitConverters.getItemStackConverter()
)
BukkitConverters.getListConverter(BukkitConverters.getItemStackConverter())
);
}
@@ -428,12 +373,9 @@ public class PacketContainer implements Serializable {
* @return A modifier for maps of statistics.
*/
public StructureModifier<Map<WrappedStatistic, Integer>> getStatisticMaps() {
return structureModifier.withType(Map.class,
BukkitConverters.<WrappedStatistic, Integer>getMapConverter(
MinecraftReflection.getStatisticClass(),
BukkitConverters.getWrappedStatisticConverter()
)
);
return getMaps(
BukkitConverters.getWrappedStatisticConverter(),
Converters.passthrough(Integer.class));
}
/**
@@ -445,7 +387,7 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<WorldType> getWorldTypeModifier() {
// Convert to and from the Bukkit wrapper
return structureModifier.<WorldType>withType(
return structureModifier.withType(
MinecraftReflection.getWorldTypeClass(),
BukkitConverters.getWorldTypeConverter());
}
@@ -456,7 +398,7 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<WrappedDataWatcher> getDataWatcherModifier() {
// Convert to and from the Bukkit wrapper
return structureModifier.<WrappedDataWatcher>withType(
return structureModifier.withType(
MinecraftReflection.getDataWatcherClass(),
BukkitConverters.getDataWatcherConverter());
}
@@ -475,7 +417,7 @@ public class PacketContainer implements Serializable {
public StructureModifier<Entity> getEntityModifier(@Nonnull World world) {
Preconditions.checkNotNull(world, "world cannot be NULL.");
// Convert to and from the Bukkit wrapper
return structureModifier.<Entity>withType(
return structureModifier.withType(
int.class, BukkitConverters.getEntityConverter(world));
}
@@ -547,10 +489,7 @@ public class PacketContainer implements Serializable {
// Convert to and from the ProtocolLib wrapper
return structureModifier.withType(
Collection.class,
BukkitConverters.getListConverter(
MinecraftReflection.getNBTBaseClass(),
BukkitConverters.getNbtConverter()
)
BukkitConverters.getListConverter(BukkitConverters.getNbtConverter())
);
}
@@ -576,9 +515,7 @@ public class PacketContainer implements Serializable {
// Convert to and from the ProtocolLib wrapper
return structureModifier.withType(
Collection.class,
BukkitConverters.getListConverter(
MinecraftReflection.getAttributeSnapshotClass(),
BukkitConverters.getWrappedAttributeConverter())
BukkitConverters.getListConverter(BukkitConverters.getWrappedAttributeConverter())
);
}
@@ -594,10 +531,7 @@ public class PacketContainer implements Serializable {
// Convert to and from the ProtocolLib wrapper
return structureModifier.withType(
Collection.class,
BukkitConverters.getListConverter(
MinecraftReflection.getChunkPositionClass(),
ChunkPosition.getConverter())
);
BukkitConverters.getListConverter(ChunkPosition.getConverter()));
}
/**
@@ -612,10 +546,7 @@ public class PacketContainer implements Serializable {
// Convert to and from the ProtocolLib wrapper
return structureModifier.withType(
Collection.class,
BukkitConverters.getListConverter(
MinecraftReflection.getBlockPositionClass(),
BlockPosition.getConverter())
);
BukkitConverters.getListConverter(BlockPosition.getConverter()));
}
/**
@@ -629,10 +560,7 @@ public class PacketContainer implements Serializable {
// Convert to and from the ProtocolLib wrapper
return structureModifier.withType(
Collection.class,
BukkitConverters.getListConverter(
MinecraftReflection.getDataWatcherItemClass(),
BukkitConverters.getWatchableObjectConverter())
);
BukkitConverters.getListConverter(BukkitConverters.getWatchableObjectConverter()));
}
/**
@@ -644,7 +572,7 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<Material> getBlocks() {
// Convert to and from the Bukkit wrapper
return structureModifier.<Material>withType(
return structureModifier.withType(
MinecraftReflection.getBlockClass(), BukkitConverters.getBlockConverter());
}
@@ -657,7 +585,7 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<WrappedGameProfile> getGameProfiles() {
// Convert to and from the Bukkit wrapper
return structureModifier.<WrappedGameProfile>withType(
return structureModifier.withType(
MinecraftReflection.getGameProfileClass(), BukkitConverters.getWrappedGameProfileConverter());
}
@@ -670,7 +598,7 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<WrappedBlockData> getBlockData() {
// Convert to and from our wrapper
return structureModifier.<WrappedBlockData>withType(
return structureModifier.withType(
MinecraftReflection.getIBlockDataClass(), BukkitConverters.getWrappedBlockDataConverter());
}
@@ -685,7 +613,7 @@ public class PacketContainer implements Serializable {
ChunkCoordIntPair chunk = getChunkCoordIntPairs().read(0);
// Convert to and from our wrapper
return structureModifier.<MultiBlockChangeInfo[]>withType(
return structureModifier.withType(
MinecraftReflection.getMultiBlockChangeInfoArrayClass(), MultiBlockChangeInfo.getArrayConverter(chunk));
}
@@ -698,7 +626,7 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<WrappedChatComponent> getChatComponents() {
// Convert to and from the Bukkit wrapper
return structureModifier.<WrappedChatComponent>withType(
return structureModifier.withType(
MinecraftReflection.getIChatBaseComponentClass(), BukkitConverters.getWrappedChatComponentConverter());
}
@@ -715,22 +643,23 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<WrappedChatComponent[]> getChatComponentArrays() {
// Convert to and from the Bukkit wrapper
return structureModifier.<WrappedChatComponent[]>withType(
return structureModifier.withType(
ComponentArrayConverter.getGenericType(),
BukkitConverters.getIgnoreNull(new ComponentArrayConverter()));
Converters.ignoreNull(new ComponentArrayConverter()));
}
/**
* Retrieve a read/write structure for the ServerPing fields in the following packet: <br>
* <ul>
* <li>{@link PacketType.Status.Server#OUT_SERVER_INFO}
* <li>{@link PacketType.Status.Server#SERVER_INFO}
* </ul>
* @return A modifier for ServerPing fields.
*/
public StructureModifier<WrappedServerPing> getServerPings() {
// Convert to and from the wrapper
return structureModifier.<WrappedServerPing>withType(
MinecraftReflection.getServerPingClass(), BukkitConverters.getWrappedServerPingConverter());
return structureModifier.withType(
MinecraftReflection.getServerPingClass(),
BukkitConverters.getWrappedServerPingConverter());
}
/**
@@ -744,10 +673,7 @@ public class PacketContainer implements Serializable {
// Convert to and from the ProtocolLib wrapper
return structureModifier.withType(
Collection.class,
BukkitConverters.getListConverter(
MinecraftReflection.getPlayerInfoDataClass(),
PlayerInfoData.getConverter())
);
BukkitConverters.getListConverter(PlayerInfoData.getConverter()));
}
/**
@@ -756,8 +682,9 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<Protocol> getProtocols() {
// Convert to and from the wrapper
return structureModifier.<Protocol>withType(
EnumWrappers.getProtocolClass(), EnumWrappers.getProtocolConverter());
return structureModifier.withType(
EnumWrappers.getProtocolClass(),
EnumWrappers.getProtocolConverter());
}
/**
@@ -766,8 +693,9 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<ClientCommand> getClientCommands() {
// Convert to and from the wrapper
return structureModifier.<ClientCommand>withType(
EnumWrappers.getClientCommandClass(), EnumWrappers.getClientCommandConverter());
return structureModifier.withType(
EnumWrappers.getClientCommandClass(),
EnumWrappers.getClientCommandConverter());
}
/**
@@ -776,8 +704,9 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<ChatVisibility> getChatVisibilities() {
// Convert to and from the wrapper
return structureModifier.<ChatVisibility>withType(
EnumWrappers.getChatVisibilityClass(), EnumWrappers.getChatVisibilityConverter());
return structureModifier.withType(
EnumWrappers.getChatVisibilityClass(),
EnumWrappers.getChatVisibilityConverter());
}
/**
@@ -786,8 +715,9 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<Difficulty> getDifficulties() {
// Convert to and from the wrapper
return structureModifier.<Difficulty>withType(
EnumWrappers.getDifficultyClass(), EnumWrappers.getDifficultyConverter());
return structureModifier.withType(
EnumWrappers.getDifficultyClass(),
EnumWrappers.getDifficultyConverter());
}
/**
@@ -796,8 +726,9 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<EntityUseAction> getEntityUseActions() {
// Convert to and from the wrapper
return structureModifier.<EntityUseAction>withType(
EnumWrappers.getEntityUseActionClass(), EnumWrappers.getEntityUseActionConverter());
return structureModifier.withType(
EnumWrappers.getEntityUseActionClass(),
EnumWrappers.getEntityUseActionConverter());
}
/**
@@ -806,8 +737,9 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<NativeGameMode> getGameModes() {
// Convert to and from the wrapper
return structureModifier.<NativeGameMode>withType(
EnumWrappers.getGameModeClass(), EnumWrappers.getGameModeConverter());
return structureModifier.withType(
EnumWrappers.getGameModeClass(),
EnumWrappers.getGameModeConverter());
}
/**
@@ -816,8 +748,9 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<ResourcePackStatus> getResourcePackStatus() {
// Convert to and from the wrapper
return structureModifier.<ResourcePackStatus>withType(
EnumWrappers.getResourcePackStatusClass(), EnumWrappers.getResourcePackStatusConverter());
return structureModifier.withType(
EnumWrappers.getResourcePackStatusClass(),
EnumWrappers.getResourcePackStatusConverter());
}
/**
@@ -826,8 +759,9 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<PlayerInfoAction> getPlayerInfoAction() {
// Convert to and from the wrapper
return structureModifier.<PlayerInfoAction>withType(
EnumWrappers.getPlayerInfoActionClass(), EnumWrappers.getPlayerInfoActionConverter());
return structureModifier.withType(
EnumWrappers.getPlayerInfoActionClass(),
EnumWrappers.getPlayerInfoActionConverter());
}
/**
@@ -836,8 +770,9 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<TitleAction> getTitleActions() {
// Convert to and from the wrapper
return structureModifier.<TitleAction>withType(
EnumWrappers.getTitleActionClass(), EnumWrappers.getTitleActionConverter());
return structureModifier.withType(
EnumWrappers.getTitleActionClass(),
EnumWrappers.getTitleActionConverter());
}
/**
@@ -846,8 +781,9 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<WorldBorderAction> getWorldBorderActions() {
// Convert to and from the wrapper
return structureModifier.<WorldBorderAction>withType(
EnumWrappers.getWorldBorderActionClass(), EnumWrappers.getWorldBorderActionConverter());
return structureModifier.withType(
EnumWrappers.getWorldBorderActionClass(),
EnumWrappers.getWorldBorderActionConverter());
}
/**
@@ -856,8 +792,9 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<CombatEventType> getCombatEvents() {
// Convert to and from the wrapper
return structureModifier.<CombatEventType>withType(
EnumWrappers.getCombatEventTypeClass(), EnumWrappers.getCombatEventTypeConverter());
return structureModifier.withType(
EnumWrappers.getCombatEventTypeClass(),
EnumWrappers.getCombatEventTypeConverter());
}
/**
@@ -866,8 +803,9 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<PlayerDigType> getPlayerDigTypes() {
// Convert to and from the wrapper
return structureModifier.<PlayerDigType>withType(
EnumWrappers.getPlayerDigTypeClass(), EnumWrappers.getPlayerDiggingActionConverter());
return structureModifier.withType(
EnumWrappers.getPlayerDigTypeClass(),
EnumWrappers.getPlayerDiggingActionConverter());
}
/**
@@ -876,8 +814,9 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<PlayerAction> getPlayerActions() {
// Convert to and from the wrapper
return structureModifier.<PlayerAction>withType(
EnumWrappers.getPlayerActionClass(), EnumWrappers.getEntityActionConverter());
return structureModifier.withType(
EnumWrappers.getPlayerActionClass(),
EnumWrappers.getEntityActionConverter());
}
/**
@@ -886,18 +825,32 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<ScoreboardAction> getScoreboardActions() {
// Convert to and from the wrapper
return structureModifier.<ScoreboardAction>withType(
EnumWrappers.getScoreboardActionClass(), EnumWrappers.getUpdateScoreActionConverter());
return structureModifier.withType(
EnumWrappers.getScoreboardActionClass(),
EnumWrappers.getUpdateScoreActionConverter());
}
/**
* Retrieve a read/write structure for the Particle enum in 1.8.
* Retrieve a read/write structure for the Particle enum in 1.8-1.12.
* <b>NOTE:</b> This will produce undesirable results in 1.13
* @return A modifier for Particle enum fields.
*/
public StructureModifier<Particle> getParticles() {
public StructureModifier<EnumWrappers.Particle> getParticles() {
// Convert to and from the wrapper
return structureModifier.<Particle>withType(
EnumWrappers.getParticleClass(), EnumWrappers.getParticleConverter());
return structureModifier.withType(
EnumWrappers.getParticleClass(),
EnumWrappers.getParticleConverter());
}
/**
* Retrieve a read/write structure for ParticleParams in 1.13
* @return A modifier for ParticleParam fields.
*/
public StructureModifier<WrappedParticle> getNewParticles() {
return structureModifier.withType(
MinecraftReflection.getMinecraftClass("ParticleParam"),
BukkitConverters.getParticleConverter()
);
}
/**
@@ -906,8 +859,9 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<PotionEffectType> getEffectTypes() {
// Convert to and from Bukkit
return structureModifier.<PotionEffectType>withType(
MinecraftReflection.getMobEffectListClass(), BukkitConverters.getEffectTypeConverter());
return structureModifier.withType(
MinecraftReflection.getMobEffectListClass(),
BukkitConverters.getEffectTypeConverter());
}
/**
@@ -916,8 +870,9 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<SoundCategory> getSoundCategories() {
// Convert to and from the enums
return structureModifier.<SoundCategory>withType(
EnumWrappers.getSoundCategoryClass(), EnumWrappers.getSoundCategoryConverter());
return structureModifier.withType(
EnumWrappers.getSoundCategoryClass(),
EnumWrappers.getSoundCategoryConverter());
}
/**
@@ -926,8 +881,9 @@ public class PacketContainer implements Serializable {
*/
public StructureModifier<Sound> getSoundEffects() {
// Convert to and from Bukkit
return structureModifier.<Sound>withType(
MinecraftReflection.getSoundEffectClass(), BukkitConverters.getSoundConverter());
return structureModifier.withType(
MinecraftReflection.getSoundEffectClass(),
BukkitConverters.getSoundConverter());
}
/**
@@ -935,8 +891,9 @@ public class PacketContainer implements Serializable {
* @return A modifier for ItemSlot enum fields.
*/
public StructureModifier<ItemSlot> getItemSlots() {
return structureModifier.<ItemSlot>withType(
EnumWrappers.getItemSlotClass(), EnumWrappers.getItemSlotConverter());
return structureModifier.withType(
EnumWrappers.getItemSlotClass(),
EnumWrappers.getItemSlotConverter());
}
/**
@@ -944,8 +901,9 @@ public class PacketContainer implements Serializable {
* @return A modifier for Hand enum fields.
*/
public StructureModifier<Hand> getHands() {
return structureModifier.<Hand>withType(
EnumWrappers.getHandClass(), EnumWrappers.getHandConverter());
return structureModifier.withType(
EnumWrappers.getHandClass(),
EnumWrappers.getHandConverter());
}
/**
@@ -953,8 +911,9 @@ public class PacketContainer implements Serializable {
* @return A modifier for Direction enum fields.
*/
public StructureModifier<Direction> getDirections() {
return structureModifier.<Direction>withType(
EnumWrappers.getDirectionClass(), EnumWrappers.getDirectionConverter());
return structureModifier.withType(
EnumWrappers.getDirectionClass(),
EnumWrappers.getDirectionConverter());
}
/**
@@ -962,8 +921,75 @@ public class PacketContainer implements Serializable {
* @return A modifier for ChatType enum fields.
*/
public StructureModifier<ChatType> getChatTypes() {
return structureModifier.<ChatType>withType(
EnumWrappers.getChatTypeClass(), EnumWrappers.getChatTypeConverter());
return structureModifier.withType(
EnumWrappers.getChatTypeClass(),
EnumWrappers.getChatTypeConverter());
}
/**
* Retrieve a read/write structure for the MinecraftKey class.
* @return A modifier for MinecraftKey fields.
*/
public StructureModifier<MinecraftKey> getMinecraftKeys() {
return structureModifier.withType(
MinecraftReflection.getMinecraftKeyClass(),
MinecraftKey.getConverter());
}
/**
* Retrive a read/write structure for dimension IDs in 1.13.1+
* @return A modifier for dimension IDs
*/
public StructureModifier<Integer> getDimensions() {
return structureModifier.withType(
MinecraftReflection.getMinecraftClass("DimensionManager"),
BukkitConverters.getDimensionIDConverter()
);
}
/**
* Retrieve a read/write structure for the Map class.
* @param keyConverter Converter for map keys
* @param valConverter Converter for map values
* @param <K> Key param
* @param <V> Value param
* @return A modifier for Map fields.
*
* @see BukkitConverters
* @see EquivalentConverter
*/
public <K, V> StructureModifier<Map<K, V>> getMaps(EquivalentConverter<K> keyConverter,
EquivalentConverter<V> valConverter) {
return structureModifier.withType(
Map.class,
BukkitConverters.getMapConverter(keyConverter, valConverter));
}
/**
* Retrieve a read/write structure for the Set class.
* @param converter Converter for elements
* @param <E> Element param
* @return A modifier for Set fields
*
* @see BukkitConverters
* @see EquivalentConverter
*/
public <E> StructureModifier<Set<E>> getSets(EquivalentConverter<E> converter) {
return structureModifier.withType(
Set.class,
BukkitConverters.getSetConverter(converter));
}
/**
* Retrieve a read/write structure for the List class.
* @param converter Converter for elements
* @param <E> Element param
* @return A modifier for List fields
*/
public <E> StructureModifier<List<E>> getLists(EquivalentConverter<E> converter) {
return structureModifier.withType(
List.class,
BukkitConverters.getListConverter(converter));
}
/**
@@ -977,7 +1003,9 @@ public class PacketContainer implements Serializable {
* @return The modifier
*/
public <T extends Enum<T>> StructureModifier<T> getEnumModifier(Class<T> enumClass, Class<?> nmsClass) {
return structureModifier.<T>withType(nmsClass, new EnumConverter<T>(enumClass));
return structureModifier.withType(
nmsClass,
new EnumConverter<>(nmsClass, enumClass));
}
/**
@@ -990,7 +1018,9 @@ public class PacketContainer implements Serializable {
* @see #getEnumModifier(Class, Class)
*/
public <T extends Enum<T>> StructureModifier<T> getEnumModifier(Class<T> enumClass, int index) {
return getEnumModifier(enumClass, structureModifier.getField(index).getType());
return getEnumModifier(
enumClass,
structureModifier.getField(index).getType());
}
/**
@@ -1036,7 +1066,7 @@ public class PacketContainer implements Serializable {
* @return A deep copy of the current packet.
*/
public PacketContainer deepClone() {
Object clonedPacket = null;
Object clonedPacket;
// Fall back on the alternative (but slower) method of reading and writing back the packet
if (CLONING_UNSUPPORTED.contains(type)) {
@@ -1156,21 +1186,52 @@ public class PacketContainer implements Serializable {
}
// ---- Metadata
// This map will only be initialized if it is actually used
private Map<String, Object> metadata;
/**
* Gets the metadata value for a given key if it exists. Packet metadata expires after a minute, which is far longer
* than a packet will ever be held in processing.
*
* @param key Metadata key
* @param <T> Metadata type
* @return The metadata value, or an empty optional
*/
public <T> Optional<T> getMeta(String key) {
return PacketMetadata.get(handle, key);
}
/**
* Sets the metadata value at a given key. Packet metadata expires after a minute, which is far longer than a packet
* will ever be held in processing.
*
* @param key Metadata key
* @param value Metadata value
* @param <T> Metadata type
*/
public <T> void setMeta(String key, T value) {
PacketMetadata.set(handle, key, value);
}
/**
* Removes the metadata for a given key if it exists.
* @param key Key to remove meta for
*/
public void removeMeta(String key) {
PacketMetadata.remove(handle, key);
}
// ---- Old Metadata API
// Scheduled for removal in 4.5
/**
* Gets the metadata value for a given key.
*
* @param key Metadata key
* @return Metadata value, or null if nonexistent.
* @deprecated Replaced with {@link #getMeta(String)}
*/
@Deprecated
public Object getMetadata(String key) {
if (metadata != null) {
return metadata.get(key);
}
return null;
return getMeta(key).orElse(null);
}
/**
@@ -1180,13 +1241,11 @@ public class PacketContainer implements Serializable {
*
* @param key Metadata key
* @param value Metadata value
* @deprecated Replaced by {@link #setMeta(String, Object)}
*/
@Deprecated
public void addMetadata(String key, Object value) {
if (metadata == null) {
metadata = new HashMap<String, Object>();
}
metadata.put(key, value);
setMeta(key, value);
}
/**
@@ -1196,18 +1255,11 @@ public class PacketContainer implements Serializable {
*
* @param key Metadata key
* @return The previous value, or null if nonexistant.
* @deprecated Replaced by {@link #removeMeta(String)}. This one was pretty much just for naming consistency.
*/
@Deprecated
public Object removeMetadata(String key) {
if (metadata != null) {
Object value = metadata.remove(key);
if (metadata.isEmpty()) {
metadata = null;
}
return value;
}
return null;
return PacketMetadata.remove(handle, key).orElseGet(null);
}
/**
@@ -1215,9 +1267,11 @@ public class PacketContainer implements Serializable {
*
* @param key Metadata key
* @return True if this packet has metadata for the key, false if not.
* @deprecated Replaced with {@code getMeta(key).isPresent()}
*/
@Deprecated
public boolean hasMetadata(String key) {
return metadata != null && metadata.containsKey(key);
return getMeta(key).isPresent();
}
/**
@@ -1259,13 +1313,13 @@ public class PacketContainer implements Serializable {
final EquivalentConverter<ItemStack> stackConverter = BukkitConverters.getItemStackConverter();
@Override
public Object getGeneric(Class<?>genericType, ItemStack[] specific) {
public Object getGeneric(ItemStack[] specific) {
Class<?> nmsStack = MinecraftReflection.getItemStackClass();
Object[] result = (Object[]) Array.newInstance(nmsStack, specific.length);
// Unwrap every item
for (int i = 0; i < result.length; i++) {
result[i] = stackConverter.getGeneric(nmsStack, specific[i]);
result[i] = stackConverter.getGeneric(specific[i]);
}
return result;
}
@@ -1296,13 +1350,13 @@ public class PacketContainer implements Serializable {
final EquivalentConverter<WrappedChatComponent> componentConverter = BukkitConverters.getWrappedChatComponentConverter();
@Override
public Object getGeneric(Class<?> genericType, WrappedChatComponent[] specific) {
public Object getGeneric(WrappedChatComponent[] specific) {
Class<?> nmsComponent = MinecraftReflection.getIChatBaseComponentClass();
Object[] result = (Object[]) Array.newInstance(nmsComponent, specific.length);
// Unwrap every item
for (int i = 0; i < result.length; i++) {
result[i] = componentConverter.getGeneric(nmsComponent, specific[i]);
result[i] = componentConverter.getGeneric(specific[i]);
}
return result;
}
@@ -1351,7 +1405,7 @@ public class PacketContainer implements Serializable {
}
@Override
public Object getGeneric(Class<?> genericType, WrappedChatComponent[] specific) {
public Object getGeneric(WrappedChatComponent[] specific) {
NbtCompound compound = NbtFactory.ofCompound("");
for (int i = 0; i < lines; i++) {
@@ -1365,7 +1419,7 @@ public class PacketContainer implements Serializable {
compound.put("Text" + (i + 1), component.getJson());
}
return nbtConverter.getGeneric(genericType, compound);
return nbtConverter.getGeneric(compound);
}
@Override
@@ -1381,7 +1435,7 @@ public class PacketContainer implements Serializable {
private static class ComponentArrayConverter implements EquivalentConverter<WrappedChatComponent[]> {
private static final EquivalentConverter<WrappedChatComponent[]> DELEGATE;
static {
if (!PacketType.Play.Server.UPDATE_SIGN.isDeprecated()) {
if (MinecraftReflection.signUpdateExists()) {
DELEGATE = new LegacyComponentConverter();
} else {
DELEGATE = new NBTComponentConverter();
@@ -1394,8 +1448,8 @@ public class PacketContainer implements Serializable {
}
@Override
public Object getGeneric(Class<?> genericType, WrappedChatComponent[] specific) {
return DELEGATE.getGeneric(genericType, specific);
public Object getGeneric(WrappedChatComponent[] specific) {
return DELEGATE.getGeneric(specific);
}
@Override
@@ -33,6 +33,7 @@ import com.comphenix.protocol.async.AsyncMarker;
import com.comphenix.protocol.error.PluginContext;
import com.comphenix.protocol.error.Report;
import com.comphenix.protocol.error.ReportType;
import com.comphenix.protocol.injector.server.TemporaryPlayer;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap;
@@ -321,7 +322,41 @@ public class PacketEvent extends EventObject implements Cancellable {
* @return The player associated with this event.
*/
public Player getPlayer() {
return playerReference.get();
Player player = playerReference.get();
// Check if the player has been updated so we can do more stuff
if (player instanceof TemporaryPlayer) {
Player updated = player.getPlayer();
if (updated != null && !(updated instanceof TemporaryPlayer)) {
playerReference.clear();
playerReference = new WeakReference<>(updated);
return updated;
}
}
return player;
}
/**
* Whether or not the player in this PacketEvent is temporary (i.e. they don't have a true player instance yet).
* Temporary players have a limited subset of methods that may be used:
* <ul>
* <li>getPlayer</li>
* <li>getAddress</li>
* <li>getServer</li>
* <li>chat</li>
* <li>sendMessage</li>
* <li>kickPlayer</li>
* <li>isOnline</li>
* </ul>
*
* Anything else will throw an UnsupportedOperationException. Use this check before calling other methods when
* dealing with packets early in the login sequence or if you get the aforementioned exception.
*
* @return True if the player is temporary, false if not.
*/
public boolean isPlayerTemporary() {
return getPlayer() instanceof TemporaryPlayer;
}
/**
@@ -0,0 +1,143 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2017 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.events;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.apache.commons.lang3.Validate;
/**
* Stores and retrieves metadata for applicable packet objects.
* @author dmulloy2
*/
class PacketMetadata {
private static class MetaObject<T> {
private final String key;
private final T value;
private MetaObject(String key, T value) {
this.key = key;
this.value = value;
}
@Override
public int hashCode() {
return Objects.hash(key, value);
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (o instanceof MetaObject) {
MetaObject that = (MetaObject) o;
return that.key.equals(this.key) &&
that.value.equals(this.value);
}
return false;
}
@Override
public String toString() {
return "MetaObject[" + key + "=" + value + "]";
}
}
// Packet meta cache
private static Cache<Object, List<MetaObject>> META_CACHE;
public static <T> Optional<T> get(Object packet, String key) {
Validate.notNull(key, "Null keys are not permitted!");
if (META_CACHE == null) {
return Optional.empty();
}
List<MetaObject> meta = META_CACHE.getIfPresent(packet);
if (meta == null) {
return Optional.empty();
}
for (MetaObject object : meta) {
if (object.key.equals(key)) {
return Optional.of((T) object.value);
}
}
return Optional.empty();
}
private static void createCache() {
META_CACHE = CacheBuilder
.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES)
.build();
}
public static <T> void set(Object packet, String key, T value) {
Validate.notNull(key, "Null keys are not permitted!");
if (META_CACHE == null) {
createCache();
}
List<MetaObject> packetMeta;
try {
packetMeta = META_CACHE.get(packet, ArrayList::new);
} catch (ExecutionException ex) {
// Not possible, but let's humor the array list constructor having an issue
packetMeta = new ArrayList<>();
}
packetMeta.removeIf(meta -> meta.key.equals(key));
packetMeta.add(new MetaObject<>(key, value));
META_CACHE.put(packet, packetMeta);
}
public static <T> Optional<T> remove(Object packet, String key) {
Validate.notNull(key, "Null keys are not permitted!");
if (META_CACHE == null) {
return Optional.empty();
}
List<MetaObject> packetMeta = META_CACHE.getIfPresent(packet);
if (packetMeta == null) {
return Optional.empty();
}
Optional<T> value = Optional.empty();
Iterator<MetaObject> iter = packetMeta.iterator();
while (iter.hasNext()) {
MetaObject meta = iter.next();
if (meta.key.equals(key)) {
value = Optional.of((T) meta.value);
iter.remove();
}
}
return value;
}
}
@@ -1,255 +1,263 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.netty;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.lang.reflect.Method;
import java.util.Arrays;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.utility.MinecraftReflection;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
/**
* A packet represented only by its id and bytes.
* @author dmulloy2
*/
public class WirePacket {
private final int id;
private final byte[] bytes;
/**
* Constructs a new WirePacket with a given type and contents
* @param type Type of the packet
* @param bytes Contents of the packet
*/
public WirePacket(PacketType type, byte[] bytes) {
this.id = checkNotNull(type, "type cannot be null").getCurrentId();
this.bytes = bytes;
}
/**
* Constructs a new WirePacket with a given id and contents
* @param id ID of the packet
* @param bytes Contents of the packet
*/
public WirePacket(int id, byte[] bytes) {
this.id = id;
this.bytes = bytes;
}
/**
* Gets this packet's ID
* @return The ID
*/
public int getId() {
return id;
}
/**
* Gets this packet's contents as a byte array
* @return The contents
*/
public byte[] getBytes() {
return bytes;
}
/**
* Writes the id of this packet to a given output
* @param output Output to write to
*/
public void writeId(ByteBuf output) {
writeVarInt(output, id);
}
/**
* Writes the contents of this packet to a given output
* @param output Output to write to
*/
public void writeBytes(ByteBuf output) {
checkNotNull(output, "output cannot be null!");
output.writeBytes(bytes);
}
/**
* Fully writes the ID and contents of this packet to a given output
* @param output Output to write to
*/
public void writeFully(ByteBuf output) {
writeId(output);
writeBytes(output);
}
/**
* Serializes this packet into a byte buffer
* @return The buffer
*/
public ByteBuf serialize() {
ByteBuf buffer = Unpooled.buffer();
writeFully(buffer);
return buffer;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj instanceof WirePacket) {
WirePacket that = (WirePacket) obj;
return this.id == that.id &&
Arrays.equals(this.bytes, that.bytes);
}
return false;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(bytes);
result = prime * result + id;
return result;
}
@Override
public String toString() {
return "WirePacket[id=" + id + ", bytes=" + Arrays.toString(bytes) + "]";
}
private static byte[] getBytes(ByteBuf buffer) {
byte[] array = new byte[buffer.readableBytes()];
buffer.readBytes(array);
return array;
}
/**
* Creates a WirePacket from an existing PacketContainer
* @param packet Existing packet
* @return The resulting WirePacket
*/
public static WirePacket fromPacket(PacketContainer packet) {
int id = packet.getType().getCurrentId();
return new WirePacket(id, bytesFromPacket(packet));
}
/**
* Creates a byte array from an existing PacketContainer containing all the
* bytes from that packet
*
* @param packet Existing packet
* @return The ByteBuf
*/
public static byte[] bytesFromPacket(PacketContainer packet) {
checkNotNull(packet, "packet cannot be null!");
ByteBuf buffer = PacketContainer.createPacketBuffer();
ByteBuf store = PacketContainer.createPacketBuffer();
// Read the bytes once
Method write = MinecraftMethods.getPacketWriteByteBufMethod();
try {
write.invoke(packet.getHandle(), buffer);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to read packet contents.", ex);
}
byte[] bytes = getBytes(buffer);
// Rewrite them to the packet to avoid issues with certain packets
if (packet.getType() == PacketType.Play.Server.CUSTOM_PAYLOAD
|| packet.getType() == PacketType.Play.Client.CUSTOM_PAYLOAD) {
// Make a copy of the array before writing
byte[] ret = Arrays.copyOf(bytes, bytes.length);
store.writeBytes(bytes);
Method read = MinecraftMethods.getPacketReadByteBufMethod();
try {
read.invoke(packet.getHandle(), store);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to rewrite packet contents.", ex);
}
return ret;
}
return bytes;
}
/**
* Creates a WirePacket from an existing Minecraft packet
* @param packet Existing Minecraft packet
* @return The resulting WirePacket
* @throws IllegalArgumentException If the packet is null or not a Minecraft packet
*/
public static WirePacket fromPacket(Object packet) {
checkNotNull(packet, "packet cannot be null!");
checkArgument(MinecraftReflection.isPacketClass(packet), "packet must be a Minecraft packet");
PacketType type = PacketType.fromClass(packet.getClass());
int id = type.getCurrentId();
ByteBuf buffer = PacketContainer.createPacketBuffer();
Method write = MinecraftMethods.getPacketWriteByteBufMethod();
try {
write.invoke(packet, buffer);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to serialize packet contents.", ex);
}
return new WirePacket(id, getBytes(buffer));
}
public static void writeVarInt(ByteBuf output, int i) {
checkNotNull(output, "output cannot be null!");
while ((i & -128) != 0) {
output.writeByte(i & 127 | 128);
i >>>= 7;
}
output.writeByte(i);
}
public static int readVarInt(ByteBuf input) {
checkNotNull(input, "input cannot be null!");
int i = 0;
int j = 0;
byte b0;
do {
b0 = input.readByte();
i |= (b0 & 127) << j++ * 7;
if (j > 5) {
throw new RuntimeException("VarInt too big");
}
} while ((b0 & 128) == 128);
return i;
}
}
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2015 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.injector.netty;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.lang.reflect.Method;
import java.util.Arrays;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.utility.MinecraftMethods;
import com.comphenix.protocol.utility.MinecraftReflection;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
/**
* A packet represented only by its id and bytes.
* @author dmulloy2
*/
public class WirePacket {
private final int id;
private final byte[] bytes;
/**
* Constructs a new WirePacket with a given type and contents
* @param type Type of the packet
* @param bytes Contents of the packet
*/
public WirePacket(PacketType type, byte[] bytes) {
this.id = checkNotNull(type, "type cannot be null").getCurrentId();
this.bytes = bytes;
}
/**
* Constructs a new WirePacket with a given id and contents
* @param id ID of the packet
* @param bytes Contents of the packet
*/
public WirePacket(int id, byte[] bytes) {
this.id = id;
this.bytes = bytes;
}
/**
* Gets this packet's ID
* @return The ID
*/
public int getId() {
return id;
}
/**
* Gets this packet's contents as a byte array
* @return The contents
*/
public byte[] getBytes() {
return bytes;
}
/**
* Writes the id of this packet to a given output
* @param output Output to write to
*/
public void writeId(ByteBuf output) {
writeVarInt(output, id);
}
/**
* Writes the contents of this packet to a given output
* @param output Output to write to
*/
public void writeBytes(ByteBuf output) {
checkNotNull(output, "output cannot be null!");
output.writeBytes(bytes);
}
/**
* Fully writes the ID and contents of this packet to a given output
* @param output Output to write to
*/
public void writeFully(ByteBuf output) {
writeId(output);
writeBytes(output);
}
/**
* Serializes this packet into a byte buffer
* @return The buffer
*/
public ByteBuf serialize() {
ByteBuf buffer = Unpooled.buffer();
writeFully(buffer);
return buffer;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj instanceof WirePacket) {
WirePacket that = (WirePacket) obj;
return this.id == that.id &&
Arrays.equals(this.bytes, that.bytes);
}
return false;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(bytes);
result = prime * result + id;
return result;
}
@Override
public String toString() {
return "WirePacket[id=" + id + ", bytes=" + Arrays.toString(bytes) + "]";
}
private static byte[] getBytes(ByteBuf buffer) {
byte[] array = new byte[buffer.readableBytes()];
buffer.readBytes(array);
return array;
}
/**
* Creates a WirePacket from an existing PacketContainer
* @param packet Existing packet
* @return The resulting WirePacket
*/
public static WirePacket fromPacket(PacketContainer packet) {
int id = packet.getType().getCurrentId();
return new WirePacket(id, bytesFromPacket(packet));
}
/**
* Creates a byte array from an existing PacketContainer containing all the
* bytes from that packet
*
* @param packet Existing packet
* @return the byte array
*/
public static byte[] bytesFromPacket(PacketContainer packet) {
checkNotNull(packet, "packet cannot be null!");
ByteBuf buffer = PacketContainer.createPacketBuffer();
ByteBuf store = PacketContainer.createPacketBuffer();
// Read the bytes once
Method write = MinecraftMethods.getPacketWriteByteBufMethod();
try {
write.invoke(packet.getHandle(), buffer);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to read packet contents.", ex);
}
byte[] bytes = getBytes(buffer);
buffer.release();
// Rewrite them to the packet to avoid issues with certain packets
if (packet.getType() == PacketType.Play.Server.CUSTOM_PAYLOAD
|| packet.getType() == PacketType.Play.Client.CUSTOM_PAYLOAD) {
// Make a copy of the array before writing
byte[] ret = Arrays.copyOf(bytes, bytes.length);
store.writeBytes(bytes);
Method read = MinecraftMethods.getPacketReadByteBufMethod();
try {
read.invoke(packet.getHandle(), store);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to rewrite packet contents.", ex);
}
return ret;
}
store.release();
return bytes;
}
/**
* Creates a WirePacket from an existing Minecraft packet
* @param packet Existing Minecraft packet
* @return The resulting WirePacket
* @throws IllegalArgumentException If the packet is null or not a Minecraft packet
*/
public static WirePacket fromPacket(Object packet) {
checkNotNull(packet, "packet cannot be null!");
checkArgument(MinecraftReflection.isPacketClass(packet), "packet must be a Minecraft packet");
PacketType type = PacketType.fromClass(packet.getClass());
int id = type.getCurrentId();
ByteBuf buffer = PacketContainer.createPacketBuffer();
Method write = MinecraftMethods.getPacketWriteByteBufMethod();
try {
write.invoke(packet, buffer);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to serialize packet contents.", ex);
}
byte[] bytes = getBytes(buffer);
buffer.release();
return new WirePacket(id, bytes);
}
public static void writeVarInt(ByteBuf output, int i) {
checkNotNull(output, "output cannot be null!");
while ((i & -128) != 0) {
output.writeByte(i & 127 | 128);
i >>>= 7;
}
output.writeByte(i);
}
public static int readVarInt(ByteBuf input) {
checkNotNull(input, "input cannot be null!");
int i = 0;
int j = 0;
byte b0;
do {
b0 = input.readByte();
i |= (b0 & 127) << j++ * 7;
if (j > 5) {
throw new RuntimeException("VarInt too big");
}
} while ((b0 & 128) == 128);
return i;
}
}
@@ -4,10 +4,10 @@ import java.lang.reflect.InvocationTargetException;
import java.net.Socket;
import java.net.SocketAddress;
import org.bukkit.entity.Player;
import com.comphenix.protocol.events.NetworkMarker;
import org.bukkit.entity.Player;
/**
* Represents an injector that only gives access to a player's socket.
*
@@ -1,19 +1,19 @@
package com.comphenix.protocol.injector.server;
/**
* Able to store a socket injector.
* A temporary player created by ProtocolLib when a true player instance does not exist.
* <p>
* A necessary hack.
* @author Kristian
* Also able to store a socket injector
* </p>
*/
class InjectorContainer {
public class TemporaryPlayer {
private volatile SocketInjector injector;
public SocketInjector getInjector() {
SocketInjector getInjector() {
return injector;
}
public void setInjector(SocketInjector injector) {
void setInjector(SocketInjector injector) {
if (injector == null)
throw new IllegalArgumentException("Injector cannot be NULL.");
this.injector = injector;
@@ -4,16 +4,11 @@ import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;
import net.sf.cglib.asm.ClassReader;
import net.sf.cglib.asm.MethodVisitor;
import net.sf.cglib.asm.Opcodes;
import net.sf.cglib.asm.Type;
import com.comphenix.protocol.reflect.ClassAnalyser.AsmMethod.AsmOpcodes;
import com.comphenix.protocol.reflect.compiler.EmptyClassVisitor;
import com.comphenix.protocol.reflect.compiler.EmptyMethodVisitor;
import com.google.common.collect.Lists;
import net.sf.cglib.asm.*;
public class ClassAnalyser {
/**
* Represents a method in ASM.
@@ -31,11 +26,11 @@ public class ClassAnalyser {
public static AsmOpcodes fromIntOpcode(int opcode) {
switch (opcode) {
case Opcodes.INVOKEVIRTUAL: return AsmOpcodes.INVOKE_VIRTUAL;
case Opcodes.INVOKESPECIAL: return AsmOpcodes.INVOKE_SPECIAL;
case Opcodes.INVOKESTATIC: return AsmOpcodes.INVOKE_STATIC;
case Opcodes.INVOKEINTERFACE: return AsmOpcodes.INVOKE_INTERFACE;
case Opcodes.INVOKEDYNAMIC: return AsmOpcodes.INVOKE_DYNAMIC;
case $Opcodes.INVOKEVIRTUAL: return AsmOpcodes.INVOKE_VIRTUAL;
case $Opcodes.INVOKESPECIAL: return AsmOpcodes.INVOKE_SPECIAL;
case $Opcodes.INVOKESTATIC: return AsmOpcodes.INVOKE_STATIC;
case $Opcodes.INVOKEINTERFACE: return AsmOpcodes.INVOKE_INTERFACE;
case $Opcodes.INVOKEDYNAMIC: return AsmOpcodes.INVOKE_DYNAMIC;
default: throw new IllegalArgumentException("Unknown opcode: " + opcode);
}
}
@@ -109,30 +104,29 @@ public class ClassAnalyser {
* @return The method calls.
* @throws IOException Cannot access the parent class.
*/
public List<AsmMethod> getMethodCalls(Class<?> clazz, Method method) throws IOException {
final ClassReader reader = new ClassReader(clazz.getCanonicalName());
private List<AsmMethod> getMethodCalls(Class<?> clazz, Method method) throws IOException {
final $ClassReader reader = new $ClassReader(clazz.getCanonicalName());
final List<AsmMethod> output = Lists.newArrayList();
// The method we are looking for
final String methodName = method.getName();
final String methodDescription = Type.getMethodDescriptor(method);
reader.accept(new EmptyClassVisitor() {
final String methodDescription = $Type.getMethodDescriptor(method);
reader.accept(new $ClassVisitor($Opcodes.ASM5) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
// Check method
public $MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (methodName.equals(name) && methodDescription.equals(desc)) {
return new EmptyMethodVisitor() {
return new $MethodVisitor($Opcodes.ASM5) {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean flag) {
output.add(new AsmMethod(AsmOpcodes.fromIntOpcode(opcode), owner, methodName, desc));
}
};
}
return null;
}
}, ClassReader.EXPAND_FRAMES);
}, $ClassReader.EXPAND_FRAMES);
return output;
}
}
@@ -21,32 +21,30 @@ package com.comphenix.protocol.reflect;
* Interface that converts generic objects into types and back.
*
* @author Kristian
* @param <TType> The specific type.
* @param <T> The specific type.
*/
public interface EquivalentConverter<TType> {
/**
* Retrieve a copy of the specific type using an instance of the generic type.
* <p>
* This is usually a wrapper type in the Bukkit API.
* @param generic - the generic type.
* @return The new specific type.
*/
public TType getSpecific(Object generic);
public interface EquivalentConverter<T> {
/**
* Retrieve a copy of the generic type from a specific type.
* <p>
* This is usually a native net.minecraft.server type in Minecraft.
* @param genericType - class or super class of the generic type.
* @param specific - the specific type we need to copy.
* @return A copy of the specific type.
*/
public Object getGeneric(Class<?> genericType, TType specific);
Object getGeneric(T specific);
/**
* Due to type erasion, we need to explicitly keep a reference to the specific type.
* Retrieve a copy of the specific type using an instance of the generic type.
* <p>
* This is usually a wrapper type in the Bukkit API or ProtocolLib API.
* @param generic - the generic type.
* @return The new specific type.
*/
T getSpecific(Object generic);
/**
* Due to type erasure, we need to explicitly keep a reference to the specific type.
* @return The specific type.
*/
public Class<TType> getSpecificType();
}
Class<T> getSpecificType();
}
@@ -19,10 +19,7 @@ package com.comphenix.protocol.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
@@ -263,6 +260,24 @@ public class StructureModifier<TField> {
return null;
}
}
/**
* Reads the value of a field only if it exists. If the field does not exist, an empty {@link Optional} is returned.
* <p>
* This method has the same functionality as {@link #readSafely(int)}, but enforces null checks by way of an Optional.
* It will eventually become the preferred method of reading fields.
*
* @param fieldIndex index of the field
* @return An optional that may contain the value of the field
* @see #readSafely(int)
*/
public Optional<TField> optionRead(int fieldIndex) {
try {
return Optional.ofNullable(read(fieldIndex));
} catch (FieldAccessException ex) {
return Optional.empty();
}
}
/**
* Determine whether or not a field is read-only (final).
@@ -352,7 +367,7 @@ public class StructureModifier<TField> {
throw new FieldAccessException(String.format("Field index out of bounds. (Index: %s, Size: %s)", fieldIndex, data.size()));
// Use the converter, if it exists
Object obj = needConversion() ? converter.getGeneric(getFieldType(fieldIndex), value) : value;
Object obj = needConversion() ? converter.getGeneric(value) : value;
try {
FieldUtils.writeField(data.get(fieldIndex), target, obj, true);
@@ -254,10 +254,16 @@ public class AggregateCloner implements Cloner {
// Make sure the object is valid
if (index < cloners.size()) {
return cloners.get(index).clone(source);
Cloner cloner = cloners.get(index);
try {
return cloner.clone(source);
} catch (Exception ex) {
throw new RuntimeException("Failed to clone " + source + " (" + source.getClass() + ") with " + cloner, ex);
}
}
// Damn - failure
throw new IllegalArgumentException("Cannot clone " + source + ": No cloner is suitable.");
throw new IllegalArgumentException("Cannot clone " + source + " ( " + source.getClass() + "): No cloner is suitable.");
}
}
@@ -21,17 +21,14 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.function.Supplier;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.BlockPosition;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.ChunkPosition;
import com.comphenix.protocol.wrappers.MinecraftKey;
import com.comphenix.protocol.wrappers.WrappedBlockData;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedServerPing;
import com.comphenix.protocol.wrappers.*;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.google.common.collect.Maps;
/**
@@ -40,58 +37,56 @@ import com.google.common.collect.Maps;
* @author Kristian
*/
public class BukkitCloner implements Cloner {
// List of classes we support
private final Map<Integer, Class<?>> clonableClasses = Maps.newConcurrentMap();
private static final Map<Class<?>, Function<Object, Object>> CLONERS = Maps.newConcurrentMap();
public BukkitCloner() {
addClass(0, MinecraftReflection.getItemStackClass());
addClass(1, MinecraftReflection.getDataWatcherClass());
// Try to add position classes
private static void fromWrapper(Supplier<Class<?>> getClass, Function<Object, ClonableWrapper> fromHandle) {
try {
addClass(2, MinecraftReflection.getBlockPositionClass());
} catch (Throwable ex) {
}
try {
addClass(3, MinecraftReflection.getChunkPositionClass());
} catch (Throwable ex) {
}
if (MinecraftReflection.isUsingNetty()) {
addClass(4, MinecraftReflection.getServerPingClass());
}
if (MinecraftReflection.watcherObjectExists()) {
addClass(5, MinecraftReflection.getDataWatcherSerializerClass());
addClass(6, MinecraftReflection.getMinecraftKeyClass());
}
try {
addClass(7, MinecraftReflection.getIBlockDataClass());
} catch (Throwable ex) {
}
try {
addClass(8, MinecraftReflection.getNonNullListClass());
} catch (Throwable ex) {
}
Class<?> nmsClass = getClass.get();
if (nmsClass != null) {
CLONERS.put(nmsClass, nmsObject -> fromHandle.apply(nmsObject).deepClone().getHandle());
}
} catch (RuntimeException ignored) { }
}
private void addClass(int id, Class<?> clazz) {
if (clazz != null)
clonableClasses.put(id, clazz);
private static void fromConverter(Supplier<Class<?>> getClass, EquivalentConverter converter) {
try {
Class<?> nmsClass = getClass.get();
if (nmsClass != null) {
CLONERS.put(nmsClass, nmsObject -> converter.getGeneric(converter.getSpecific(nmsObject)));
}
} catch (RuntimeException ignored) { }
}
private int findMatchingClass(Class<?> type) {
// See if is a subclass of any of our supported superclasses
for (Entry<Integer, Class<?>> entry : clonableClasses.entrySet()) {
if (entry.getValue().isAssignableFrom(type)) {
return entry.getKey();
private static void fromManual(Supplier<Class<?>> getClass, Function<Object, Object> cloner) {
try {
Class<?> nmsClass = getClass.get();
if (nmsClass != null) {
CLONERS.put(nmsClass, cloner);
}
} catch (RuntimeException ignored) { }
}
static {
fromManual(MinecraftReflection::getItemStackClass, source ->
MinecraftReflection.getMinecraftItemStack(MinecraftReflection.getBukkitItemStack(source).clone()));
fromWrapper(MinecraftReflection::getDataWatcherClass, WrappedDataWatcher::new);
fromConverter(MinecraftReflection::getBlockPositionClass, BlockPosition.getConverter());
fromConverter(MinecraftReflection::getChunkPositionClass, ChunkPosition.getConverter());
fromWrapper(MinecraftReflection::getServerPingClass, WrappedServerPing::fromHandle);
fromConverter(MinecraftReflection::getMinecraftKeyClass, MinecraftKey.getConverter());
fromWrapper(MinecraftReflection::getIBlockDataClass, WrappedBlockData::fromHandle);
fromManual(MinecraftReflection::getNonNullListClass, source -> nonNullListCloner().clone(source));
fromWrapper(MinecraftReflection::getNBTBaseClass, NbtFactory::fromNMS);
}
private Function<Object, Object> findCloner(Class<?> type) {
for (Entry<Class<?>, Function<Object, Object>> entry : CLONERS.entrySet()) {
if (entry.getKey().isAssignableFrom(type)) {
return entry.getValue();
}
}
return -1;
return null;
}
@Override
@@ -99,7 +94,7 @@ public class BukkitCloner implements Cloner {
if (source == null)
return false;
return findMatchingClass(source.getClass()) >= 0;
return findCloner(source.getClass()) != null;
}
@Override
@@ -107,40 +102,12 @@ public class BukkitCloner implements Cloner {
if (source == null)
throw new IllegalArgumentException("source cannot be NULL.");
// Convert to a wrapper
switch (findMatchingClass(source.getClass())) {
case 0:
return MinecraftReflection.getMinecraftItemStack(MinecraftReflection.getBukkitItemStack(source).clone());
case 1:
EquivalentConverter<WrappedDataWatcher> dataConverter = BukkitConverters.getDataWatcherConverter();
return dataConverter.getGeneric(clonableClasses.get(1), dataConverter.getSpecific(source).deepClone());
case 2:
EquivalentConverter<BlockPosition> blockConverter = BlockPosition.getConverter();
return blockConverter.getGeneric(clonableClasses.get(2), blockConverter.getSpecific(source));
case 3:
EquivalentConverter<ChunkPosition> chunkConverter = ChunkPosition.getConverter();
return chunkConverter.getGeneric(clonableClasses.get(3), chunkConverter.getSpecific(source));
case 4:
EquivalentConverter<WrappedServerPing> serverConverter = BukkitConverters.getWrappedServerPingConverter();
return serverConverter.getGeneric(clonableClasses.get(4), serverConverter.getSpecific(source).deepClone());
case 5:
return source;
case 6:
EquivalentConverter<MinecraftKey> keyConverter = MinecraftKey.getConverter();
return keyConverter.getGeneric(clonableClasses.get(6), keyConverter.getSpecific(source));
case 7:
EquivalentConverter<WrappedBlockData> blockDataConverter = BukkitConverters.getWrappedBlockDataConverter();
return blockDataConverter.getGeneric(clonableClasses.get(7), blockDataConverter.getSpecific(source).deepClone());
case 8:
return nonNullListCloner().clone(source);
default:
throw new IllegalArgumentException("Cannot clone objects of type " + source.getClass());
}
return findCloner(source.getClass()).apply(source);
}
private static Constructor<?> nonNullList = null;
private static final Cloner nonNullListCloner() {
private static Cloner nonNullListCloner() {
return new Cloner() {
@Override
public boolean canClone(Object source) {
@@ -20,10 +20,13 @@ package com.comphenix.protocol.reflect.cloning;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import com.google.common.collect.BiMap;
/**
* Attempts to clone collection and array classes.
*
@@ -48,72 +51,89 @@ public class CollectionCloner implements Cloner {
Class<?> clazz = source.getClass();
return Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz) || clazz.isArray();
}
@SuppressWarnings("unchecked")
@Override
@SuppressWarnings("unchecked")
public Object clone(Object source) {
if (source == null)
throw new IllegalArgumentException("source cannot be NULL.");
Class<?> clazz = source.getClass();
if (source instanceof Collection) {
Collection<Object> copy = cloneConstructor(Collection.class, clazz, source);
try {
if (source instanceof Collection) {
Collection<Object> copy = cloneConstructor(Collection.class, clazz, source);
// Next, clone each element in the collection
try {
copy.clear();
for (Object element : (Collection<Object>) source) {
copy.add(getClone(element, source));
// Next, clone each element in the collection
try {
copy.clear();
for (Object element : (Collection<Object>) source) {
copy.add(getClone(element, source));
}
} catch (UnsupportedOperationException e) {
// Immutable - we can't do much about that
}
} catch (UnsupportedOperationException e) {
// Immutable - we can't do much about that
}
return copy;
} else if (source instanceof Map) {
Map<Object, Object> copy = cloneConstructor(Map.class, clazz, source);
// Next, clone each element in the collection
try {
copy.clear();
for (Entry<Object, Object> element : ((Map<Object, Object>) source).entrySet()) {
Object key = getClone(element.getKey(), source);
Object value = getClone(element.getValue(), source);
copy.put(key, value);
return copy;
} else if (source instanceof Map) {
Map<Object, Object> copy;
if (source instanceof BiMap) {
BiMap original = (BiMap) source;
if (clazz.getDeclaringClass() != null) {
Method create = clazz.getDeclaringClass().getMethod("create", int.class);
copy = ((BiMap) create.invoke(null, original.size())).inverse();
} else {
Method create = clazz.getMethod("create", int.class);
copy = (Map) create.invoke(null, original.size());
}
} else {
copy = cloneConstructor(Map.class, clazz, source);
}
} catch (UnsupportedOperationException e) {
// Immutable - we can't do much about that
// Next, clone each element in the collection
try {
copy.clear();
for (Entry<Object, Object> element : ((Map<Object, Object>) source).entrySet()) {
Object key = getClone(element.getKey(), source);
Object value = getClone(element.getValue(), source);
copy.put(key, value);
}
} catch (UnsupportedOperationException e) {
// Immutable - we can't do much about that
}
return copy;
} else if (clazz.isArray()) {
// Get the length
int length = Array.getLength(source);
Class<?> component = clazz.getComponentType();
// Can we speed things up by making a shallow copy instead?
if (ImmutableDetector.isImmutable(component)) {
return clonePrimitive(component, source);
}
// Create a new copy
Object copy = Array.newInstance(clazz.getComponentType(), length);
// Set each element
for (int i = 0; i < length; i++) {
Object element = Array.get(source, i);
if (defaultCloner.canClone(element))
Array.set(copy, i, defaultCloner.clone(element));
else
throw new IllegalArgumentException("Cannot clone " + element + " in array " + source);
}
// And we're done
return copy;
}
return copy;
} else if (clazz.isArray()) {
// Get the length
int lenght = Array.getLength(source);
Class<?> component = clazz.getComponentType();
// Can we speed things up by making a shallow copy instead?
if (ImmutableDetector.isImmutable(component)) {
return clonePrimitive(component, source);
}
// Create a new copy
Object copy = Array.newInstance(clazz.getComponentType(), lenght);
// Set each element
for (int i = 0; i < lenght; i++) {
Object element = Array.get(source, i);
if (defaultCloner.canClone(element))
Array.set(copy, i, defaultCloner.clone(element));
else
throw new IllegalArgumentException("Cannot clone " + element + " in array " + source);
}
// And we're done
return copy;
} catch (Exception ex) {
throw new RuntimeException("Failed to clone " + source + " (" + source.getClass() + ")", ex);
}
throw new IllegalArgumentException(source + " is not an array nor a Collection.");
@@ -171,7 +191,7 @@ public class CollectionCloner implements Cloner {
private <T> T cloneConstructor(Class<?> superclass, Class<?> clazz, Object source) {
// Not all collections or maps implement "clone", but most *do* implement the "copy constructor" pattern
try {
Constructor<?> constructCopy = clazz.getConstructor(Collection.class);
Constructor<?> constructCopy = clazz.getConstructor(superclass);
return (T) constructCopy.newInstance(source);
} catch (NoSuchMethodException e) {
if (source instanceof Serializable)
@@ -186,7 +206,7 @@ public class CollectionCloner implements Cloner {
/**
* Clone an object by calling "clone" using reflection.
* @param clazz - the class type.
* @param obj - the object to clone.
* @param source - the object to clone.
* @return The cloned object.
*/
private Object cloneObject(Class<?> clazz, Object source) {
@@ -6,14 +6,14 @@ package com.comphenix.protocol.reflect.cloning;
import com.google.common.base.Optional;
/**
* A cloner that can clone Optional objects
* A cloner that can clone Guava Optional objects
* @author dmulloy2
*/
public class OptionalCloner implements Cloner {
public class GuavaOptionalCloner implements Cloner {
protected Cloner wrapped;
public OptionalCloner(Cloner wrapped) {
public GuavaOptionalCloner(Cloner wrapped) {
this.wrapped = wrapped;
}
@@ -26,11 +26,16 @@ import java.net.URI;
import java.net.URL;
import java.security.PublicKey;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;
import java.util.function.Supplier;
import javax.crypto.SecretKey;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.primitives.Primitives;
/**
@@ -42,19 +47,45 @@ import com.google.common.primitives.Primitives;
*/
public class ImmutableDetector implements Cloner {
// Notable immutable classes we might encounter
private static final Class<?>[] immutableClasses = {
StackTraceElement.class, BigDecimal.class,
BigInteger.class, Locale.class, UUID.class,
URL.class, URI.class, Inet4Address.class,
Inet6Address.class, InetSocketAddress.class,
SecretKey.class, PublicKey.class
};
private static final Set<Class<?>> immutableClasses = ImmutableSet.of(
StackTraceElement.class, BigDecimal.class,
BigInteger.class, Locale.class, UUID.class,
URL.class, URI.class, Inet4Address.class,
Inet6Address.class, InetSocketAddress.class,
SecretKey.class, PublicKey.class
);
private static final Set<Class<?>> immutableNMS = Sets.newConcurrentHashSet();
static {
add(MinecraftReflection::getGameProfileClass);
add(MinecraftReflection::getDataWatcherSerializerClass);
add(() -> MinecraftReflection.getMinecraftClass("SoundEffect"));
add(MinecraftReflection::getBlockClass);
add(MinecraftReflection::getItemClass);
if (MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE)) {
add(() -> MinecraftReflection.getMinecraftClass("Particle"));
add(MinecraftReflection::getFluidTypeClass);
add(MinecraftReflection::getParticleTypeClass);
}
}
private static void add(Supplier<Class<?>> getClass) {
try {
Class<?> clazz = getClass.get();
if (clazz != null) {
immutableNMS.add(clazz);
}
} catch (RuntimeException ignored) { }
}
@Override
public boolean canClone(Object source) {
// Don't accept NULL
if (source == null)
if (source == null) {
return false;
}
return isImmutable(source.getClass());
}
@@ -66,32 +97,31 @@ public class ImmutableDetector implements Cloner {
*/
public static boolean isImmutable(Class<?> type) {
// Cases that are definitely not true
if (type.isArray())
if (type.isArray()) {
return false;
}
// All primitive types
if (Primitives.isWrapperType(type) || String.class.equals(type))
if (Primitives.isWrapperType(type) || String.class.equals(type)) {
return true;
// May not be true, but if so, that kind of code is broken anyways
if (isEnumWorkaround(type))
return true;
for (Class<?> clazz : immutableClasses)
if (clazz.equals(type))
return true;
// Check for known immutable classes in 1.7.2
if (MinecraftReflection.isUsingNetty()) {
if (type.equals(MinecraftReflection.getGameProfileClass())) {
return true;
}
}
// Check for known immutable classes in 1.9
if (MinecraftReflection.watcherObjectExists()) {
if (type.equals(MinecraftReflection.getDataWatcherSerializerClass())
|| type.equals(MinecraftReflection.getMinecraftClass("SoundEffect"))) {
// May not be true, but if so, that kind of code is broken anyways
if (isEnumWorkaround(type)) {
return true;
}
// No good way to clone lambdas
if (type.getName().contains("$$Lambda$")) {
return true;
}
if (immutableClasses.contains(type)) {
return true;
}
for (Class<?> clazz : immutableNMS) {
if (MinecraftReflection.is(clazz, type)) {
return true;
}
}
@@ -103,10 +133,13 @@ public class ImmutableDetector implements Cloner {
// This is just great. Just great.
private static boolean isEnumWorkaround(Class<?> enumClass) {
while (enumClass != null) {
if (enumClass.isEnum())
if (enumClass.isEnum()) {
return true;
}
enumClass = enumClass.getSuperclass();
}
return false;
}
@@ -0,0 +1,33 @@
/**
* (c) 2018 dmulloy2
*/
package com.comphenix.protocol.reflect.cloning;
import java.util.Optional;
/**
* A cloner that can clone Java Optional objects
* @author dmulloy2
*/
public class JavaOptionalCloner implements Cloner {
protected Cloner wrapped;
public JavaOptionalCloner(Cloner wrapped) {
this.wrapped = wrapped;
}
@Override
public boolean canClone(Object source) {
return source instanceof Optional;
}
@Override
public Object clone(Object source) {
Optional<?> optional = (Optional<?>) source;
return optional.map(o -> wrapped.clone(o));
}
public Cloner getWrapped() {
return wrapped;
}
}
@@ -17,25 +17,25 @@
package com.comphenix.protocol.reflect.compiler;
import net.sf.cglib.asm.MethodVisitor;
import net.sf.cglib.asm.Opcodes;
import net.sf.cglib.asm.Type;
import net.sf.cglib.asm.$MethodVisitor;
import net.sf.cglib.asm.$Opcodes;
import net.sf.cglib.asm.$Type;
/**
* Used by the compiler to automatically box and unbox values.
*/
class BoxingHelper {
private final static Type BYTE_TYPE = Type.getObjectType("java/lang/Byte");
private final static Type BOOLEAN_TYPE = Type.getObjectType("java/lang/Boolean");
private final static Type SHORT_TYPE = Type.getObjectType("java/lang/Short");
private final static Type CHARACTER_TYPE = Type.getObjectType("java/lang/Character");
private final static Type INTEGER_TYPE = Type.getObjectType("java/lang/Integer");
private final static Type FLOAT_TYPE = Type.getObjectType("java/lang/Float");
private final static Type LONG_TYPE = Type.getObjectType("java/lang/Long");
private final static Type DOUBLE_TYPE = Type.getObjectType("java/lang/Double");
private final static Type NUMBER_TYPE = Type.getObjectType("java/lang/Number");
private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
private final static $Type BYTE_$Type = $Type.getObjectType("java/lang/Byte");
private final static $Type BOOLEAN_$Type = $Type.getObjectType("java/lang/Boolean");
private final static $Type SHORT_$Type = $Type.getObjectType("java/lang/Short");
private final static $Type CHARACTER_$Type = $Type.getObjectType("java/lang/Character");
private final static $Type INTEGER_$Type = $Type.getObjectType("java/lang/Integer");
private final static $Type FLOAT_$Type = $Type.getObjectType("java/lang/Float");
private final static $Type LONG_$Type = $Type.getObjectType("java/lang/Long");
private final static $Type DOUBLE_$Type = $Type.getObjectType("java/lang/Double");
private final static $Type NUMBER_$Type = $Type.getObjectType("java/lang/Number");
private final static $Type OBJECT_$Type = $Type.getObjectType("java/lang/Object");
private final static MethodDescriptor BOOLEAN_VALUE = MethodDescriptor.getMethod("boolean booleanValue()");
private final static MethodDescriptor CHAR_VALUE = MethodDescriptor.getMethod("char charValue()");
@@ -44,9 +44,9 @@ class BoxingHelper {
private final static MethodDescriptor LONG_VALUE = MethodDescriptor.getMethod("long longValue()");
private final static MethodDescriptor DOUBLE_VALUE = MethodDescriptor.getMethod("double doubleValue()");
private MethodVisitor mv;
private $MethodVisitor mv;
public BoxingHelper(MethodVisitor mv) {
public BoxingHelper($MethodVisitor mv) {
this.mv = mv;
}
@@ -54,42 +54,42 @@ class BoxingHelper {
* Generates the instructions to box the top stack value. This value is
* replaced by its boxed equivalent on top of the stack.
*
* @param type the type of the top stack value.
* @param type the $Type of the top stack value.
*/
public void box(final Type type){
if(type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
public void box(final $Type type){
if(type.getSort() == $Type.OBJECT || type.getSort() == $Type.ARRAY) {
return;
}
if(type == Type.VOID_TYPE) {
if(type == $Type.VOID_TYPE) {
push((String) null);
} else {
Type boxed = type;
$Type boxed = type;
switch(type.getSort()) {
case Type.BYTE:
boxed = BYTE_TYPE;
case $Type.BYTE:
boxed = BYTE_$Type;
break;
case Type.BOOLEAN:
boxed = BOOLEAN_TYPE;
case $Type.BOOLEAN:
boxed = BOOLEAN_$Type;
break;
case Type.SHORT:
boxed = SHORT_TYPE;
case $Type.SHORT:
boxed = SHORT_$Type;
break;
case Type.CHAR:
boxed = CHARACTER_TYPE;
case $Type.CHAR:
boxed = CHARACTER_$Type;
break;
case Type.INT:
boxed = INTEGER_TYPE;
case $Type.INT:
boxed = INTEGER_$Type;
break;
case Type.FLOAT:
boxed = FLOAT_TYPE;
case $Type.FLOAT:
boxed = FLOAT_$Type;
break;
case Type.LONG:
boxed = LONG_TYPE;
case $Type.LONG:
boxed = LONG_$Type;
break;
case Type.DOUBLE:
boxed = DOUBLE_TYPE;
case $Type.DOUBLE:
boxed = DOUBLE_$Type;
break;
}
@@ -105,46 +105,46 @@ class BoxingHelper {
swap();
}
invokeConstructor(boxed, new MethodDescriptor("<init>", Type.VOID_TYPE, new Type[] {type}));
invokeConstructor(boxed, new MethodDescriptor("<init>", $Type.VOID_TYPE, new $Type[] {type}));
}
}
/**
* Generates the instruction to invoke a constructor.
*
* @param type the class in which the constructor is defined.
* @param $Type the class in which the constructor is defined.
* @param method the constructor to be invoked.
*/
public void invokeConstructor(final Type type, final MethodDescriptor method){
invokeInsn(Opcodes.INVOKESPECIAL, type, method);
public void invokeConstructor(final $Type $Type, final MethodDescriptor method){
invokeInsn($Opcodes.INVOKESPECIAL, $Type, method);
}
/**
* Generates a DUP_X1 instruction.
*/
public void dupX1(){
mv.visitInsn(Opcodes.DUP_X1);
mv.visitInsn($Opcodes.DUP_X1);
}
/**
* Generates a DUP_X2 instruction.
*/
public void dupX2(){
mv.visitInsn(Opcodes.DUP_X2);
mv.visitInsn($Opcodes.DUP_X2);
}
/**
* Generates a POP instruction.
*/
public void pop(){
mv.visitInsn(Opcodes.POP);
mv.visitInsn($Opcodes.POP);
}
/**
* Generates a SWAP instruction.
*/
public void swap(){
mv.visitInsn(Opcodes.SWAP);
mv.visitInsn($Opcodes.SWAP);
}
/**
@@ -163,11 +163,11 @@ class BoxingHelper {
*/
public void push(final int value) {
if (value >= -1 && value <= 5) {
mv.visitInsn(Opcodes.ICONST_0 + value);
mv.visitInsn($Opcodes.ICONST_0 + value);
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
mv.visitIntInsn(Opcodes.BIPUSH, value);
mv.visitIntInsn($Opcodes.BIPUSH, value);
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
mv.visitIntInsn(Opcodes.SIPUSH, value);
mv.visitIntInsn($Opcodes.SIPUSH, value);
} else {
mv.visitLdcInsn(new Integer(value));
}
@@ -176,10 +176,10 @@ class BoxingHelper {
/**
* Generates the instruction to create a new object.
*
* @param type the class of the object to be created.
* @param $Type the class of the object to be created.
*/
public void newInstance(final Type type){
typeInsn(Opcodes.NEW, type);
public void newInstance(final $Type $Type){
$TypeInsn($Opcodes.NEW, $Type);
}
/**
@@ -189,7 +189,7 @@ class BoxingHelper {
*/
public void push(final String value) {
if (value == null) {
mv.visitInsn(Opcodes.ACONST_NULL);
mv.visitInsn($Opcodes.ACONST_NULL);
} else {
mv.visitLdcInsn(value);
}
@@ -200,35 +200,35 @@ class BoxingHelper {
* replaced by its unboxed equivalent on top of the stack.
*
* @param type
* the type of the top stack value.
* the $Type of the top stack value.
*/
public void unbox(final Type type){
Type t = NUMBER_TYPE;
public void unbox(final $Type type){
$Type t = NUMBER_$Type;
MethodDescriptor sig = null;
switch(type.getSort()) {
case Type.VOID:
case $Type.VOID:
return;
case Type.CHAR:
t = CHARACTER_TYPE;
case $Type.CHAR:
t = CHARACTER_$Type;
sig = CHAR_VALUE;
break;
case Type.BOOLEAN:
t = BOOLEAN_TYPE;
case $Type.BOOLEAN:
t = BOOLEAN_$Type;
sig = BOOLEAN_VALUE;
break;
case Type.DOUBLE:
case $Type.DOUBLE:
sig = DOUBLE_VALUE;
break;
case Type.FLOAT:
case $Type.FLOAT:
sig = FLOAT_VALUE;
break;
case Type.LONG:
case $Type.LONG:
sig = LONG_VALUE;
break;
case Type.INT:
case Type.SHORT:
case Type.BYTE:
case $Type.INT:
case $Type.SHORT:
case $Type.BYTE:
sig = INT_VALUE;
}
@@ -242,13 +242,13 @@ class BoxingHelper {
/**
* Generates the instruction to check that the top stack value is of the
* given type.
* given $Type.
*
* @param type a class or interface type.
* @param $Type a class or interface $Type.
*/
public void checkCast(final Type type){
if(!type.equals(OBJECT_TYPE)) {
typeInsn(Opcodes.CHECKCAST, type);
public void checkCast(final $Type $Type){
if(!$Type.equals(OBJECT_$Type)) {
$TypeInsn($Opcodes.CHECKCAST, $Type);
}
}
@@ -258,35 +258,35 @@ class BoxingHelper {
* @param owner the class in which the method is defined.
* @param method the method to be invoked.
*/
public void invokeVirtual(final Type owner, final MethodDescriptor method){
invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method);
public void invokeVirtual(final $Type owner, final MethodDescriptor method){
invokeInsn($Opcodes.INVOKEVIRTUAL, owner, method);
}
/**
* Generates an invoke method instruction.
*
* @param opcode the instruction's opcode.
* @param type the class in which the method is defined.
* @param $Type the class in which the method is defined.
* @param method the method to be invoked.
*/
private void invokeInsn(final int opcode, final Type type, final MethodDescriptor method){
String owner = type.getSort() == Type.ARRAY ? type.getDescriptor() : type.getInternalName();
private void invokeInsn(final int opcode, final $Type $Type, final MethodDescriptor method){
String owner = $Type.getSort() == $Type.ARRAY ? $Type.getDescriptor() : $Type.getInternalName();
mv.visitMethodInsn(opcode, owner, method.getName(), method.getDescriptor());
}
/**
* Generates a type dependent instruction.
* Generates a $Type dependent instruction.
*
* @param opcode the instruction's opcode.
* @param type the instruction's operand.
* @param $Type the instruction's operand.
*/
private void typeInsn(final int opcode, final Type type){
private void $TypeInsn(final int opcode, final $Type $Type){
String desc;
if(type.getSort() == Type.ARRAY) {
desc = type.getDescriptor();
if($Type.getSort() == $Type.ARRAY) {
desc = $Type.getDescriptor();
} else {
desc = type.getInternalName();
desc = $Type.getInternalName();
}
mv.visitTypeInsn(opcode, desc);
@@ -110,7 +110,7 @@ public abstract class CompiledStructureModifier extends StructureModifier<Object
@Override
public StructureModifier<Object> write(int index, Object value) throws FieldAccessException {
if (converter != null)
value = converter.getGeneric(getFieldType(index), value);
value = converter.getGeneric(value);
return writeGenerated(index, value);
}
@@ -1,57 +0,0 @@
package com.comphenix.protocol.reflect.compiler;
import net.sf.cglib.asm.AnnotationVisitor;
import net.sf.cglib.asm.Attribute;
import net.sf.cglib.asm.ClassVisitor;
import net.sf.cglib.asm.FieldVisitor;
import net.sf.cglib.asm.MethodVisitor;
public abstract class EmptyClassVisitor implements ClassVisitor {
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
// NOP
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
// NOP
return null;
}
@Override
public void visitAttribute(Attribute attr) {
// NOP
}
@Override
public void visitEnd() {
// NOP
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
// NOP
return null;
}
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
// NOP
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
// NOP
return null;
}
@Override
public void visitOuterClass(String owner, String name, String desc) {
// NOP
}
@Override
public void visitSource(String source, String debug) {
// NOP
}
}
@@ -1,132 +0,0 @@
package com.comphenix.protocol.reflect.compiler;
import net.sf.cglib.asm.AnnotationVisitor;
import net.sf.cglib.asm.Attribute;
import net.sf.cglib.asm.Label;
import net.sf.cglib.asm.MethodVisitor;
public class EmptyMethodVisitor implements MethodVisitor {
@Override
public AnnotationVisitor visitAnnotationDefault() {
// NOP
return null;
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
// NOP
return null;
}
@Override
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
// NOP
return null;
}
@Override
public void visitAttribute(Attribute attr) {
// NOP
}
@Override
public void visitCode() {
// NOP
}
@Override
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
// NOP
}
@Override
public void visitInsn(int opcode) {
// NOP
}
@Override
public void visitIntInsn(int opcode, int operand) {
// NOP
}
@Override
public void visitVarInsn(int opcode, int var) {
// NOP
}
@Override
public void visitTypeInsn(int opcode, String type) {
// NOP
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
// NOP
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
// NOP
}
@Override
public void visitJumpInsn(int opcode, Label label) {
// NOP
}
@Override
public void visitLabel(Label label) {
// NOP
}
@Override
public void visitLdcInsn(Object cst) {
// NOP
}
@Override
public void visitIincInsn(int var, int increment) {
// NOP
}
@Override
public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
// NOP
}
@Override
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
// NOP
}
@Override
public void visitMultiANewArrayInsn(String desc, int dims) {
// NOP
}
@Override
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
// NOP
}
@Override
public void visitLocalVariable(String name, String desc, String signature, Label start,
Label end, int index) {
// NOP
}
@Override
public void visitLineNumber(int line, Label start) {
// NOP
}
@Override
public void visitMaxs(int maxStack, int maxLocals) {
// NOP
}
@Override
public void visitEnd() {
// NOP
}
}
@@ -20,7 +20,7 @@ package com.comphenix.protocol.reflect.compiler;
import java.util.HashMap;
import java.util.Map;
import net.sf.cglib.asm.Type;
import net.sf.cglib.asm.$Type;
/**
* Represents a method.
@@ -56,7 +56,7 @@ class MethodDescriptor {
}
/**
* Creates a new {@link Method}.
* Creates a new {@link MethodDescriptor}.
*
* @param name the method's name.
* @param desc the method's descriptor.
@@ -67,7 +67,7 @@ class MethodDescriptor {
}
/**
* Creates a new {@link Method}.
* Creates a new {@link MethodDescriptor}.
*
* @param name the method's name.
* @param returnType the method's return type.
@@ -75,14 +75,14 @@ class MethodDescriptor {
*/
public MethodDescriptor(
final String name,
final Type returnType,
final Type[] argumentTypes)
final $Type returnType,
final $Type[] argumentTypes)
{
this(name, Type.getMethodDescriptor(returnType, argumentTypes));
this(name, $Type.getMethodDescriptor(returnType, argumentTypes));
}
/**
* Returns a {@link Method} corresponding to the given Java method
* Returns a {@link MethodDescriptor} corresponding to the given Java method
* declaration.
*
* @param method a Java method declaration, without argument names, of the
@@ -91,7 +91,7 @@ class MethodDescriptor {
* "java.util.List", ...). Classes of the java.lang package can be
* specified by their unqualified name; all other classes names must
* be fully qualified.
* @return a {@link Method} corresponding to the given Java method
* @return a {@link MethodDescriptor} corresponding to the given Java method
* declaration.
* @throws IllegalArgumentException if <code>method</code> could not get
* parsed.
@@ -103,7 +103,7 @@ class MethodDescriptor {
}
/**
* Returns a {@link Method} corresponding to the given Java method
* Returns a {@link MethodDescriptor} corresponding to the given Java method
* declaration.
*
* @param method a Java method declaration, without argument names, of the
@@ -117,7 +117,7 @@ class MethodDescriptor {
* default package, or false if they correspond to java.lang classes.
* For instance "Object" means "Object" if this option is true, or
* "java.lang.Object" otherwise.
* @return a {@link Method} corresponding to the given Java method
* @return a {@link MethodDescriptor} corresponding to the given Java method
* declaration.
* @throws IllegalArgumentException if <code>method</code> could not get
* parsed.
@@ -134,7 +134,7 @@ class MethodDescriptor {
}
String returnType = method.substring(0, space);
String methodName = method.substring(space + 1, start - 1).trim();
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder();
sb.append('(');
int p;
do {
@@ -158,7 +158,7 @@ class MethodDescriptor {
return type;
}
StringBuffer sb = new StringBuffer();
StringBuilder sb = new StringBuilder();
int index = 0;
while ((index = type.indexOf("[]", index) + 1) > 0) {
sb.append('[');
@@ -206,8 +206,8 @@ class MethodDescriptor {
*
* @return the return type of the method described by this object.
*/
public Type getReturnType() {
return Type.getReturnType(desc);
public $Type getReturnType() {
return $Type.getReturnType(desc);
}
/**
@@ -215,8 +215,8 @@ class MethodDescriptor {
*
* @return the argument types of the method described by this object.
*/
public Type[] getArgumentTypes() {
return Type.getArgumentTypes(desc);
public $Type[] getArgumentTypes() {
return $Type.getArgumentTypes(desc);
}
public String toString() {
@@ -25,12 +25,12 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.sf.cglib.asm.ClassWriter;
import net.sf.cglib.asm.FieldVisitor;
import net.sf.cglib.asm.Label;
import net.sf.cglib.asm.MethodVisitor;
import net.sf.cglib.asm.Opcodes;
import net.sf.cglib.asm.Type;
import net.sf.cglib.asm.$ClassWriter;
import net.sf.cglib.asm.$FieldVisitor;
import net.sf.cglib.asm.$Label;
import net.sf.cglib.asm.$MethodVisitor;
import net.sf.cglib.asm.$Opcodes;
import net.sf.cglib.asm.$Type;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.error.Report;
@@ -95,22 +95,22 @@ import com.google.common.primitives.Primitives;
/**
* Represents a StructureModifier compiler.
*
*
* @author Kristian
*/
public final class StructureCompiler {
public static final ReportType REPORT_TOO_MANY_GENERATED_CLASSES = new ReportType("Generated too many classes (count: %s)");
// Used to store generated classes of different types
@SuppressWarnings("rawtypes")
static class StructureKey {
private Class targetType;
private Class fieldType;
public StructureKey(StructureModifier<?> source) {
this(source.getTargetType(), source.getFieldType());
}
public StructureKey(Class targetType, Class fieldType) {
this.targetType = targetType;
this.fieldType = fieldType;
@@ -120,7 +120,7 @@ public final class StructureCompiler {
public int hashCode() {
return Objects.hashCode(targetType, fieldType);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof StructureKey) {
@@ -131,16 +131,16 @@ public final class StructureCompiler {
return false;
}
}
// Used to load classes
private volatile static Method defineMethod;
@SuppressWarnings("rawtypes")
private Map<StructureKey, Class> compiledCache = new ConcurrentHashMap<StructureKey, Class>();
// The class loader we'll store our classes
private ClassLoader loader;
// References to other classes
private static String PACKAGE_NAME = "com/comphenix/protocol/reflect/compiler";
private static String SUPER_CLASS = "com/comphenix/protocol/reflect/StructureModifier";
@@ -156,7 +156,7 @@ public final class StructureCompiler {
StructureCompiler(ClassLoader loader) {
this.loader = loader;
}
/**
* Lookup the current class loader for any previously generated classes before we attempt to generate something.
* @param <TField> Type
@@ -194,7 +194,7 @@ public final class StructureCompiler {
// We need to compile the class
return false;
}
/**
* Compiles the given structure modifier.
* <p>
@@ -211,15 +211,15 @@ public final class StructureCompiler {
if (!isAnyPublic(source.getFields())) {
return source;
}
StructureKey key = new StructureKey(source);
Class<?> compiledClass = compiledCache.get(key);
if (!compiledCache.containsKey(key)) {
compiledClass = generateClass(source);
compiledCache.put(key, compiledClass);
}
// Next, create an instance of this class
try {
return (StructureModifier<TField>) compiledClass.getConstructor(
@@ -245,7 +245,7 @@ public final class StructureCompiler {
throw new IllegalStateException("Cannot happen.", e);
}
}
/**
* Retrieve a variable identifier that can uniquely represent the given type.
* @param type - a type.
@@ -254,7 +254,7 @@ public final class StructureCompiler {
private String getSafeTypeName(Class<?> type) {
return type.getCanonicalName().replace("[]", "Array").replace(".", "_");
}
/**
* Retrieve the compiled name of a given structure modifier.
* @param source - the structure modifier.
@@ -262,29 +262,29 @@ public final class StructureCompiler {
*/
private String getCompiledName(StructureModifier<?> source) {
Class<?> targetType = source.getTargetType();
// Concat class and field type
return "CompiledStructure$" +
getSafeTypeName(targetType) + "$" +
getSafeTypeName(source.getFieldType());
}
/**
* Compile a structure modifier.
* @param source - structure modifier.
* @return The compiled structure modifier.
*/
private <TField> Class<?> generateClass(StructureModifier<TField> source) {
ClassWriter cw = new ClassWriter(0);
$ClassWriter cw = new $ClassWriter(0);
Class<?> targetType = source.getTargetType();
String className = getCompiledName(source);
String targetSignature = Type.getDescriptor(targetType);
String targetSignature = $Type.getDescriptor(targetType);
String targetName = targetType.getName().replace('.', '/');
// Define class
cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, PACKAGE_NAME + "/" + className,
cw.visit($Opcodes.V1_6, $Opcodes.ACC_PUBLIC + $Opcodes.ACC_SUPER, PACKAGE_NAME + "/" + className,
null, COMPILED_CLASS, null);
createFields(cw, targetSignature);
@@ -292,29 +292,29 @@ public final class StructureCompiler {
createReadMethod(cw, className, source.getFields(), targetSignature, targetName);
createWriteMethod(cw, className, source.getFields(), targetSignature, targetName);
cw.visitEnd();
byte[] data = cw.toByteArray();
// Call the define method
try {
if (defineMethod == null) {
Method defined = ClassLoader.class.getDeclaredMethod("defineClass",
new Class<?>[] { String.class, byte[].class, int.class, int.class });
// Awesome. Now, create and return it.
defined.setAccessible(true);
defineMethod = defined;
}
@SuppressWarnings("rawtypes")
Class clazz = (Class) defineMethod.invoke(loader, null, data, 0, data.length);
// DEBUG CODE: Print the content of the generated class.
//org.objectweb.asm.ClassReader cr = new org.objectweb.asm.ClassReader(data);
//cr.accept(new ASMifierClassVisitor(new PrintWriter(System.out)), 0);
return clazz;
} catch (SecurityException e) {
throw new RuntimeException("Cannot use reflection to dynamically load a class.", e);
} catch (NoSuchMethodException e) {
@@ -327,7 +327,7 @@ public final class StructureCompiler {
throw new RuntimeException("Error occured in code generator.", e);
}
}
/**
* Determine if at least one of the given fields is public.
* @param fields - field to test.
@@ -340,208 +340,208 @@ public final class StructureCompiler {
return true;
}
}
return false;
}
private boolean isPublic(Field field) {
return Modifier.isPublic(field.getModifiers());
}
private boolean isNonFinal(Field field) {
return !Modifier.isFinal(field.getModifiers());
}
private void createFields(ClassWriter cw, String targetSignature) {
FieldVisitor typedField = cw.visitField(Opcodes.ACC_PRIVATE, "typedTarget", targetSignature, null, null);
private void createFields($ClassWriter cw, String targetSignature) {
$FieldVisitor typedField = cw.visitField($Opcodes.ACC_PRIVATE, "typedTarget", targetSignature, null, null);
typedField.visitEnd();
}
private void createWriteMethod(ClassWriter cw, String className, List<Field> fields, String targetSignature, String targetName) {
private void createWriteMethod($ClassWriter cw, String className, List<Field> fields, String targetSignature, String targetName) {
String methodDescriptor = "(ILjava/lang/Object;)L" + SUPER_CLASS + ";";
String methodSignature = "(ILjava/lang/Object;)L" + SUPER_CLASS + "<Ljava/lang/Object;>;";
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PROTECTED, "writeGenerated", methodDescriptor, methodSignature,
$MethodVisitor mv = cw.visitMethod($Opcodes.ACC_PROTECTED, "writeGenerated", methodDescriptor, methodSignature,
new String[] { FIELD_EXCEPTION_CLASS });
BoxingHelper boxingHelper = new BoxingHelper(mv);
String generatedClassName = PACKAGE_NAME + "/" + className;
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature);
mv.visitVarInsn(Opcodes.ASTORE, 3);
mv.visitVarInsn(Opcodes.ILOAD, 1);
// The last label is for the default switch
Label[] labels = new Label[fields.size()];
Label errorLabel = new Label();
Label returnLabel = new Label();
// Generate labels
String generatedClassName = PACKAGE_NAME + "/" + className;
mv.visitCode();
mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitFieldInsn($Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature);
mv.visitVarInsn($Opcodes.ASTORE, 3);
mv.visitVarInsn($Opcodes.ILOAD, 1);
// The last $Label is for the default switch
$Label[] $Labels = new $Label[fields.size()];
$Label error$Label = new $Label();
$Label return$Label = new $Label();
// Generate $Labels
for (int i = 0; i < fields.size(); i++) {
labels[i] = new Label();
$Labels[i] = new $Label();
}
mv.visitTableSwitchInsn(0, labels.length - 1, errorLabel, labels);
mv.visitTableSwitchInsn(0, $Labels.length - 1, error$Label, $Labels);
for (int i = 0; i < fields.size(); i++) {
Field field = fields.get(i);
Class<?> outputType = field.getType();
Class<?> inputType = Primitives.wrap(outputType);
String typeDescriptor = Type.getDescriptor(outputType);
String typeDescriptor = $Type.getDescriptor(outputType);
String inputPath = inputType.getName().replace('.', '/');
mv.visitLabel(labels[i]);
mv.visitLabel($Labels[i]);
// Push the compare object
if (i == 0)
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null);
mv.visitFrame($Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null);
else
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
// Only write to public non-final fields
if (isPublic(field) && isNonFinal(field)) {
mv.visitVarInsn(Opcodes.ALOAD, 3);
mv.visitVarInsn(Opcodes.ALOAD, 2);
mv.visitVarInsn($Opcodes.ALOAD, 3);
mv.visitVarInsn($Opcodes.ALOAD, 2);
if (!outputType.isPrimitive())
mv.visitTypeInsn(Opcodes.CHECKCAST, inputPath);
mv.visitTypeInsn($Opcodes.CHECKCAST, inputPath);
else
boxingHelper.unbox(Type.getType(outputType));
mv.visitFieldInsn(Opcodes.PUTFIELD, targetName, field.getName(), typeDescriptor);
boxingHelper.unbox($Type.getType(outputType));
mv.visitFieldInsn($Opcodes.PUTFIELD, targetName, field.getName(), typeDescriptor);
} else {
// Use reflection. We don't have a choice, unfortunately.
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ILOAD, 1);
mv.visitVarInsn(Opcodes.ALOAD, 2);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, generatedClassName, "writeReflected", "(ILjava/lang/Object;)V");
mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitVarInsn($Opcodes.ILOAD, 1);
mv.visitVarInsn($Opcodes.ALOAD, 2);
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, generatedClassName, "writeReflected", "(ILjava/lang/Object;)V");
}
mv.visitJumpInsn(Opcodes.GOTO, returnLabel);
mv.visitJumpInsn($Opcodes.GOTO, return$Label);
}
mv.visitLabel(errorLabel);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitTypeInsn(Opcodes.NEW, FIELD_EXCEPTION_CLASS);
mv.visitInsn(Opcodes.DUP);
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
mv.visitInsn(Opcodes.DUP);
mv.visitLabel(error$Label);
mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
mv.visitTypeInsn($Opcodes.NEW, FIELD_EXCEPTION_CLASS);
mv.visitInsn($Opcodes.DUP);
mv.visitTypeInsn($Opcodes.NEW, "java/lang/StringBuilder");
mv.visitInsn($Opcodes.DUP);
mv.visitLdcInsn("Invalid index ");
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
mv.visitVarInsn(Opcodes.ILOAD, 1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "<init>", "(Ljava/lang/String;)V");
mv.visitInsn(Opcodes.ATHROW);
mv.visitLabel(returnLabel);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitInsn(Opcodes.ARETURN);
mv.visitMethodInsn($Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
mv.visitVarInsn($Opcodes.ILOAD, 1);
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;");
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
mv.visitMethodInsn($Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "<init>", "(Ljava/lang/String;)V");
mv.visitInsn($Opcodes.ATHROW);
mv.visitLabel(return$Label);
mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitInsn($Opcodes.ARETURN);
mv.visitMaxs(5, 4);
mv.visitEnd();
}
private void createReadMethod(ClassWriter cw, String className, List<Field> fields, String targetSignature, String targetName) {
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PROTECTED, "readGenerated", "(I)Ljava/lang/Object;", null,
private void createReadMethod($ClassWriter cw, String className, List<Field> fields, String targetSignature, String targetName) {
$MethodVisitor mv = cw.visitMethod($Opcodes.ACC_PROTECTED, "readGenerated", "(I)Ljava/lang/Object;", null,
new String[] { "com/comphenix/protocol/reflect/FieldAccessException" });
BoxingHelper boxingHelper = new BoxingHelper(mv);
String generatedClassName = PACKAGE_NAME + "/" + className;
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature);
mv.visitVarInsn(Opcodes.ASTORE, 2);
mv.visitVarInsn(Opcodes.ILOAD, 1);
// The last label is for the default switch
Label[] labels = new Label[fields.size()];
Label errorLabel = new Label();
// Generate labels
String generatedClassName = PACKAGE_NAME + "/" + className;
mv.visitCode();
mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitFieldInsn($Opcodes.GETFIELD, generatedClassName, "typedTarget", targetSignature);
mv.visitVarInsn($Opcodes.ASTORE, 2);
mv.visitVarInsn($Opcodes.ILOAD, 1);
// The last $Label is for the default switch
$Label[] $Labels = new $Label[fields.size()];
$Label error$Label = new $Label();
// Generate $Labels
for (int i = 0; i < fields.size(); i++) {
labels[i] = new Label();
$Labels[i] = new $Label();
}
mv.visitTableSwitchInsn(0, fields.size() - 1, errorLabel, labels);
mv.visitTableSwitchInsn(0, fields.size() - 1, error$Label, $Labels);
for (int i = 0; i < fields.size(); i++) {
Field field = fields.get(i);
Class<?> outputType = field.getType();
String typeDescriptor = Type.getDescriptor(outputType);
mv.visitLabel(labels[i]);
String typeDescriptor = $Type.getDescriptor(outputType);
mv.visitLabel($Labels[i]);
// Push the compare object
if (i == 0)
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null);
mv.visitFrame($Opcodes.F_APPEND, 1, new Object[] { targetName }, 0, null);
else
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
// Note that byte code cannot access non-public fields
if (isPublic(field)) {
mv.visitVarInsn(Opcodes.ALOAD, 2);
mv.visitFieldInsn(Opcodes.GETFIELD, targetName, field.getName(), typeDescriptor);
boxingHelper.box(Type.getType(outputType));
mv.visitVarInsn($Opcodes.ALOAD, 2);
mv.visitFieldInsn($Opcodes.GETFIELD, targetName, field.getName(), typeDescriptor);
boxingHelper.box($Type.getType(outputType));
} else {
// We have to use reflection for private and protected fields.
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ILOAD, 1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, generatedClassName, "readReflected", "(I)Ljava/lang/Object;");
mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitVarInsn($Opcodes.ILOAD, 1);
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, generatedClassName, "readReflected", "(I)Ljava/lang/Object;");
}
mv.visitInsn(Opcodes.ARETURN);
mv.visitInsn($Opcodes.ARETURN);
}
mv.visitLabel(errorLabel);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitTypeInsn(Opcodes.NEW, FIELD_EXCEPTION_CLASS);
mv.visitInsn(Opcodes.DUP);
mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
mv.visitInsn(Opcodes.DUP);
mv.visitLabel(error$Label);
mv.visitFrame($Opcodes.F_SAME, 0, null, 0, null);
mv.visitTypeInsn($Opcodes.NEW, FIELD_EXCEPTION_CLASS);
mv.visitInsn($Opcodes.DUP);
mv.visitTypeInsn($Opcodes.NEW, "java/lang/StringBuilder");
mv.visitInsn($Opcodes.DUP);
mv.visitLdcInsn("Invalid index ");
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
mv.visitVarInsn(Opcodes.ILOAD, 1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "<init>", "(Ljava/lang/String;)V");
mv.visitInsn(Opcodes.ATHROW);
mv.visitMethodInsn($Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
mv.visitVarInsn($Opcodes.ILOAD, 1);
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;");
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
mv.visitMethodInsn($Opcodes.INVOKESPECIAL, FIELD_EXCEPTION_CLASS, "<init>", "(Ljava/lang/String;)V");
mv.visitInsn($Opcodes.ATHROW);
mv.visitMaxs(5, 3);
mv.visitEnd();
}
private void createConstructor(ClassWriter cw, String className, String targetSignature, String targetName) {
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
private void createConstructor($ClassWriter cw, String className, String targetSignature, String targetName) {
$MethodVisitor mv = cw.visitMethod($Opcodes.ACC_PUBLIC, "<init>",
"(L" + SUPER_CLASS + ";L" + PACKAGE_NAME + "/StructureCompiler;)V",
"(L" + SUPER_CLASS + "<Ljava/lang/Object;>;L" + PACKAGE_NAME + "/StructureCompiler;)V", null);
String fullClassName = PACKAGE_NAME + "/" + className;
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, COMPILED_CLASS, "<init>", "()V");
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, fullClassName, "initialize", "(L" + SUPER_CLASS + ";)V");
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, SUPER_CLASS, "getTarget", "()Ljava/lang/Object;");
mv.visitFieldInsn(Opcodes.PUTFIELD, fullClassName, "target", "Ljava/lang/Object;");
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(Opcodes.GETFIELD, fullClassName, "target", "Ljava/lang/Object;");
mv.visitTypeInsn(Opcodes.CHECKCAST, targetName);
mv.visitFieldInsn(Opcodes.PUTFIELD, fullClassName, "typedTarget", targetSignature);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 2);
mv.visitFieldInsn(Opcodes.PUTFIELD, fullClassName, "compiler", "L" + PACKAGE_NAME + "/StructureCompiler;");
mv.visitInsn(Opcodes.RETURN);
mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitMethodInsn($Opcodes.INVOKESPECIAL, COMPILED_CLASS, "<init>", "()V");
mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitVarInsn($Opcodes.ALOAD, 1);
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, fullClassName, "initialize", "(L" + SUPER_CLASS + ";)V");
mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitVarInsn($Opcodes.ALOAD, 1);
mv.visitMethodInsn($Opcodes.INVOKEVIRTUAL, SUPER_CLASS, "getTarget", "()Ljava/lang/Object;");
mv.visitFieldInsn($Opcodes.PUTFIELD, fullClassName, "target", "Ljava/lang/Object;");
mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitFieldInsn($Opcodes.GETFIELD, fullClassName, "target", "Ljava/lang/Object;");
mv.visitTypeInsn($Opcodes.CHECKCAST, targetName);
mv.visitFieldInsn($Opcodes.PUTFIELD, fullClassName, "typedTarget", targetSignature);
mv.visitVarInsn($Opcodes.ALOAD, 0);
mv.visitVarInsn($Opcodes.ALOAD, 2);
mv.visitFieldInsn($Opcodes.PUTFIELD, fullClassName, "compiler", "L" + PACKAGE_NAME + "/StructureCompiler;");
mv.visitInsn($Opcodes.RETURN);
mv.visitMaxs(2, 3);
mv.visitEnd();
}
@@ -17,8 +17,8 @@
package com.comphenix.protocol.utility;
import java.util.Map;
import java.util.Optional;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
@@ -51,7 +51,7 @@ class CachedPackage {
*/
public void setPackageClass(String className, Class<?> clazz) {
if (clazz != null) {
cache.put(className, Optional.<Class<?>> of(clazz));
cache.put(className, Optional.of(clazz));
} else {
cache.remove(className);
}
@@ -63,32 +63,16 @@ class CachedPackage {
* @return Class object.
* @throws RuntimeException If we are unable to find the given class.
*/
public Class<?> getPackageClass(String className) {
public Optional<Class<?>> getPackageClass(final String className) {
Preconditions.checkNotNull(className, "className cannot be null!");
// See if we've already looked it up
if (cache.containsKey(className)) {
Optional<Class<?>> result = cache.get(className);
if (!result.isPresent()) {
throw new RuntimeException("Cannot find class " + className);
return cache.computeIfAbsent(className, x -> {
try {
return Optional.ofNullable(source.loadClass(combine(packageName, className)));
} catch (ClassNotFoundException ex) {
return Optional.empty();
}
return result.get();
}
try {
// Try looking it up
Class<?> clazz = source.loadClass(combine(packageName, className));
if (clazz == null) {
throw new IllegalArgumentException("Source " + source + " returned null for " + className);
}
cache.put(className, Optional.<Class<?>> of(clazz));
return clazz;
} catch (ClassNotFoundException ex) {
cache.put(className, Optional.<Class<?>> absent());
throw new RuntimeException("Cannot find class " + className, ex);
}
});
}
/**
@@ -21,7 +21,7 @@ package com.comphenix.protocol.utility;
*/
public final class Constants {
public static final String PACKAGE_VERSION = "v1_12_R1";
public static final String PACKAGE_VERSION = "v1_13_R2";
public static final String NMS = "net.minecraft.server." + PACKAGE_VERSION;
public static final String OBC = "org.bukkit.craftbukkit." + PACKAGE_VERSION;
}
@@ -1,23 +1,22 @@
package com.comphenix.protocol.utility;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.GenericFutureListener;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.GenericFutureListener;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
/**
* Static methods for accessing Minecraft methods.
*
@@ -103,10 +102,12 @@ public class MinecraftMethods {
*/
public static Method getNetworkManagerHandleMethod() {
if (networkManagerHandle == null) {
networkManagerHandle = FuzzyReflection.fromClass(MinecraftReflection.getNetworkManagerClass(), true).
getMethodByParameters("handle", MinecraftReflection.getPacketClass(), GenericFutureListener[].class);
networkManagerHandle = FuzzyReflection
.fromClass(MinecraftReflection.getNetworkManagerClass(), true)
.getMethodByParameters("handle", MinecraftReflection.getPacketClass());
networkManagerHandle.setAccessible(true);
}
return networkManagerHandle;
}
@@ -170,15 +171,16 @@ public class MinecraftMethods {
// This object will allow us to detect which methods were called
Enhancer enhancer = EnhancerFactory.getInstance().createEnhancer();
enhancer.setSuperclass(MinecraftReflection.getPacketDataSerializerClass());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (method.getName().contains("read"))
throw new ReadMethodException();
if (method.getName().contains("write"))
throw new WriteMethodException();
return proxy.invokeSuper(obj, args);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
if (method.getName().contains("read")) {
throw new ReadMethodException();
}
if (method.getName().contains("write")) {
throw new WriteMethodException();
}
return proxy.invokeSuper(obj, args);
});
// Create our proxy object
@@ -45,6 +45,10 @@ public class MinecraftProtocolVersion {
map.put(new MinecraftVersion(1, 11, 0), 315);
map.put(new MinecraftVersion(1, 11, 1), 316);
map.put(new MinecraftVersion(1, 12, 0), 335);
map.put(new MinecraftVersion(1, 12, 1), 338);
map.put(new MinecraftVersion(1, 12, 2), 340);
map.put(new MinecraftVersion(1, 13, 0), 393);
map.put(new MinecraftVersion(1, 13, 1), 401);
return map;
}
File diff suppressed because it is too large. Load diff
@@ -44,6 +44,11 @@ public class MinecraftVersion implements Comparable<MinecraftVersion>, Serializa
*/
private static final Pattern VERSION_PATTERN = Pattern.compile(".*\\(.*MC.\\s*([a-zA-z0-9\\-\\.]+)\\s*\\)");
/**
* Version 1.13 - update aquatic.
*/
public static final MinecraftVersion AQUATIC_UPDATE = new MinecraftVersion("1.13");
/**
* Version 1.12 - the world of color update.
*/
@@ -351,4 +356,8 @@ public class MinecraftVersion implements Comparable<MinecraftVersion>, Serializa
return currentVersion;
}
public static boolean atOrAbove(MinecraftVersion version) {
return getCurrentVersion().isAtLeast(version);
}
}
@@ -53,11 +53,11 @@ public class Util {
}
/**
* Whether or not this server is running Spigot. This works by checking
* the server version for the String "Spigot"
* Whether or not this server is running Spigot or a Spigot fork. This works by checking
* the server version for the Strings "Spigot" or "Paper".
* @return True if it is, false if not.
*/
public static boolean isUsingSpigot() {
return Bukkit.getServer().getVersion().contains("Spigot");
return Bukkit.getServer().getVersion().contains("Spigot") || Bukkit.getServer().getVersion().contains("Paper");
}
}
@@ -0,0 +1,175 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2017 Dan Mulloy
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
* Automatically wraps an internal NMS class to a non-versioned, deofbuscated class.
* Requirements:
* <ul>
* <li>The wrapper must be public</li>
* <li>If the wrapper is an internal class, it must be static</li>
* <li>The wrapper must have one public constructor with no arguments (the default constructor is acceptable)</li>
* <li>The wrapper must have the the same number of fields as the NMS class</li>
* <li>Each field should correspond, in order, to its NMS counterpart</li>
* <li>Non-generic fields must have a converter</li>
* </ul>
*
* @author dmulloy2
*/
public class AutoWrapper<T> implements EquivalentConverter<T> {
private Map<Integer, Function<Object, Object>> wrappers = new HashMap<>();
private Map<Integer, Function<Object, Object>> unwrappers = new HashMap<>();
private Class<T> wrapperClass;
private Class<?> nmsClass;
private AutoWrapper(Class<T> wrapperClass, Class<?> nmsClass) {
this.wrapperClass = wrapperClass;
this.nmsClass = nmsClass;
}
public static <T> AutoWrapper<T> wrap(Class<T> wrapperClass, Class<?> nmsClass) {
return new AutoWrapper<>(wrapperClass, nmsClass);
}
public static <T> AutoWrapper<T> wrap(Class<T> wrapperClass, String nmsClassName) {
return wrap(wrapperClass, MinecraftReflection.getMinecraftClass(nmsClassName));
}
public AutoWrapper<T> field(int index, Function<Object, Object> wrapper, Function<Object, Object> unwrapper) {
wrappers.put(index, wrapper);
unwrappers.put(index, unwrapper);
return this;
}
public AutoWrapper<T> field(int index, EquivalentConverter converter) {
return field(index, converter::getSpecific, specific -> converter.getGeneric(specific));
}
public T wrap(Object nmsObject) {
T instance;
try {
instance = wrapperClass.newInstance();
} catch (ReflectiveOperationException ex) {
throw new InvalidWrapperException(wrapperClass.getSimpleName() + " is not accessible!", ex);
}
Field[] wrapperFields = wrapperClass.getDeclaredFields();
Field[] nmsFields = Arrays
.stream(nmsClass.getDeclaredFields())
.filter(field -> !Modifier.isStatic(field.getModifiers()))
.toArray(Field[]::new);
for (int i = 0; i < wrapperFields.length; i++) {
try {
Field wrapperField = wrapperFields[i];
Field nmsField = nmsFields[i];
if (!nmsField.isAccessible())
nmsField.setAccessible(true);
Object value = nmsField.get(nmsObject);
if (wrappers.containsKey(i))
value = wrappers.get(i).apply(value);
wrapperField.set(instance, value);
} catch (ReflectiveOperationException ex) {
throw new InvalidWrapperException("Failed to wrap field", ex);
}
}
return instance;
}
public Object unwrap(Object wrapper) {
Object instance;
try {
instance = nmsClass.newInstance();
} catch (ReflectiveOperationException ex) {
throw new InvalidWrapperException("Failed to construct new " + nmsClass.getSimpleName(), ex);
}
Field[] wrapperFields = wrapperClass.getDeclaredFields();
Field[] nmsFields = Arrays
.stream(nmsClass.getDeclaredFields())
.filter(field -> !Modifier.isStatic(field.getModifiers()))
.toArray(Field[]::new);
for (int i = 0; i < wrapperFields.length; i++) {
try {
Field wrapperField = wrapperFields[i];
Field nmsField = nmsFields[i];
if (!nmsField.isAccessible())
nmsField.setAccessible(true);
if (Modifier.isFinal(nmsField.getModifiers()))
unsetFinal(nmsField);
Object value = wrapperField.get(wrapper);
if (unwrappers.containsKey(i))
value = unwrappers.get(i).apply(value);
nmsField.set(instance, value);
} catch (ReflectiveOperationException ex) {
throw new InvalidWrapperException("Failed to unwrap field", ex);
}
}
return instance;
}
private void unsetFinal(Field field) throws ReflectiveOperationException {
Field modifiers = Field.class.getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
}
// ---- Equivalent conversion
@Override
public T getSpecific(Object generic) {
return wrap(generic);
}
@Override
public Object getGeneric(Object specific) {
return unwrap(specific);
}
@Override
public Class<T> getSpecificType() {
return wrapperClass;
}
public static class InvalidWrapperException extends RuntimeException {
private InvalidWrapperException(String message, Throwable cause) {
super(message, cause);
}
}
}
@@ -164,7 +164,7 @@ public class BlockPosition {
public static EquivalentConverter<BlockPosition> getConverter() {
return new EquivalentConverter<BlockPosition>() {
@Override
public Object getGeneric(Class<?> genericType, BlockPosition specific) {
public Object getGeneric(BlockPosition specific) {
if (blockPositionConstructor == null) {
try {
blockPositionConstructor = MinecraftReflection.getBlockPositionClass().
@@ -187,7 +187,7 @@ public class BlockPosition {
public BlockPosition getSpecific(Object generic) {
if (MinecraftReflection.isBlockPosition(generic)) {
// Use a structure modifier
intModifier = new StructureModifier<Object>(generic.getClass(), null, false).withType(int.class);
intModifier = new StructureModifier<>(generic.getClass(), null, false).withType(int.class);
// Damn it all
if (intModifier.size() < 3) {
File diff suppressed because it is too large. Load diff
@@ -67,7 +67,7 @@ public class ChunkCoordIntPair {
public static EquivalentConverter<ChunkCoordIntPair> getConverter() {
return new EquivalentConverter<ChunkCoordIntPair>() {
@Override
public Object getGeneric(Class<?> genericType, ChunkCoordIntPair specific) {
public Object getGeneric(ChunkCoordIntPair specific) {
if (COORD_CONSTRUCTOR == null) {
COORD_CONSTRUCTOR = Accessors.getConstructorAccessor(COORD_PAIR_CLASS, int.class, int.class);
}
@@ -154,7 +154,7 @@ public class ChunkPosition {
public static EquivalentConverter<ChunkPosition> getConverter() {
return new EquivalentConverter<ChunkPosition>() {
@Override
public Object getGeneric(Class<?> genericType, ChunkPosition specific) {
public Object getGeneric(ChunkPosition specific) {
if (chunkPositionConstructor == null) {
try {
chunkPositionConstructor = MinecraftReflection.getChunkPositionClass().
@@ -177,7 +177,7 @@ public class ChunkPosition {
public ChunkPosition getSpecific(Object generic) {
if (MinecraftReflection.isChunkPosition(generic)) {
// Use a structure modifier
intModifier = new StructureModifier<Object>(generic.getClass(), null, false).withType(int.class);
intModifier = new StructureModifier<>(generic.getClass(), null, false).withType(int.class);
// Damn it all
if (intModifier.size() < 3) {
@@ -0,0 +1,7 @@
package com.comphenix.protocol.wrappers;
public interface ClonableWrapper {
Object getHandle();
ClonableWrapper deepClone();
}
@@ -20,13 +20,19 @@ import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
/**
* Handles component parsing in 1.8
* @author dmulloy2
*/
public class ComponentParser {
private static Constructor readerConstructor;
private static Method setLenient;
private static Method getAdapter;
private static Method read;
private ComponentParser() {
}
@@ -45,13 +51,21 @@ public class ComponentParser {
// Should only be needed on 1.8.
private static Object deserializeLegacy(Object gson, Class<?> component, StringReader str) {
try {
Class<?> readerClass = Class.forName("org.bukkit.craftbukkit.libs.com.google.gson.stream.JsonReader");
Object reader = readerClass.getConstructor(Reader.class).newInstance(str);
Method setLenient = readerClass.getMethod("setLenient", boolean.class);
if(readerConstructor == null){
Class<?> readerClass = Class.forName("org.bukkit.craftbukkit.libs.com.google.gson.stream.JsonReader");
readerConstructor = readerClass.getDeclaredConstructor(Reader.class);
readerConstructor.setAccessible(true);
setLenient = readerClass.getDeclaredMethod("setLenient", boolean.class);
setLenient.setAccessible(true);
getAdapter = gson.getClass().getDeclaredMethod("getAdapter", Class.class);
getAdapter.setAccessible(true);
Object adapter = getAdapter.invoke(gson, component);
read = adapter.getClass().getDeclaredMethod("read", readerClass);
read.setAccessible(true);
}
Object reader = readerConstructor.newInstance(str);
setLenient.invoke(reader, true);
Method getAdapter = gson.getClass().getMethod("getAdapter", Class.class);
Object adapter = getAdapter.invoke(gson, component);
Method read = adapter.getClass().getMethod("read", readerClass);
return read.invoke(adapter, reader);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Failed to read JSON", ex);
@@ -0,0 +1,106 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2017 Dan Mulloy
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package com.comphenix.protocol.wrappers;
import java.util.function.Function;
import com.comphenix.protocol.reflect.EquivalentConverter;
/**
* Utility class for converters
* @author dmulloy2
*/
public class Converters {
/**
* Returns a converter that ignores null elements, so that the underlying converter doesn't have to worry about them.
* @param converter Underlying converter
* @param <T> Element type
* @return An ignore null converter
*/
public static <T> EquivalentConverter<T> ignoreNull(final EquivalentConverter<T> converter) {
return new EquivalentConverter<T>() {
@Override
public T getSpecific(Object generic) {
return generic != null ? converter.getSpecific(generic) : null;
}
@Override
public Object getGeneric(T specific) {
return specific != null ? converter.getGeneric(specific) : null;
}
@Override
public Class<T> getSpecificType() {
return converter.getSpecificType();
}
};
}
/**
* Returns a converter that passes generic and specific values through without converting.
* @param clazz Element class
* @param <T> Element type
* @return A passthrough converter
*/
public static <T> EquivalentConverter<T> passthrough(final Class<T> clazz) {
return ignoreNull(new EquivalentConverter<T>() {
@Override
public T getSpecific(Object generic) {
return (T) generic;
}
@Override
public Object getGeneric(T specific) {
return specific;
}
@Override
public Class<T> getSpecificType() {
return clazz;
}
});
}
/**
* Creates a simple converter for wrappers with {@code getHandle()} and {@code fromHandle(...)} methods. With Java 8,
* converters can be reduced to a single line (see {@link BukkitConverters#getWrappedGameProfileConverter()}).
* @param toHandle Function from wrapper to handle (i.e. {@code getHandle()})
* @param fromHandle Function from handle to wrapper (i.e. {@code fromHandle(Object)})
* @param <T> Wrapper type
* @return A handle converter
*/
public static <T> EquivalentConverter<T> handle(final Function<T, Object> toHandle,
final Function<Object, T> fromHandle) {
return new EquivalentConverter<T>() {
@Override
public T getSpecific(Object generic) {
return fromHandle.apply(generic);
}
@Override
public Object getGeneric(T specific) {
return toHandle.apply(specific);
}
@Override
public Class<T> getSpecificType() {
return null;
}
};
}
}
@@ -4,15 +4,17 @@ import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.bukkit.GameMode;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.ProtocolLogger;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.Maps;
import org.bukkit.EntityEffect;
import org.bukkit.GameMode;
/**
* Represents a generic enum converter.
* @author Kristian
@@ -21,26 +23,26 @@ public abstract class EnumWrappers {
public enum ClientCommand {
PERFORM_RESPAWN,
REQUEST_STATS,
OPEN_INVENTORY_ACHIEVEMENT;
OPEN_INVENTORY_ACHIEVEMENT
}
public enum ChatVisibility {
FULL,
SYSTEM,
HIDDEN;
HIDDEN
}
public enum Difficulty {
PEACEFUL,
EASY,
NORMAL,
HARD;
HARD
}
public enum EntityUseAction {
INTERACT,
ATTACK,
INTERACT_AT;
INTERACT_AT
}
/**
@@ -110,7 +112,7 @@ public abstract class EnumWrappers {
SUCCESSFULLY_LOADED,
DECLINED,
FAILED_DOWNLOAD,
ACCEPTED;
ACCEPTED
}
public enum PlayerInfoAction {
@@ -118,15 +120,16 @@ public abstract class EnumWrappers {
UPDATE_GAME_MODE,
UPDATE_LATENCY,
UPDATE_DISPLAY_NAME,
REMOVE_PLAYER;
REMOVE_PLAYER
}
public enum TitleAction {
TITLE,
SUBTITLE,
ACTIONBAR,
TIMES,
CLEAR,
RESET;
RESET
}
public enum WorldBorderAction {
@@ -135,13 +138,13 @@ public abstract class EnumWrappers {
SET_CENTER,
INITIALIZE,
SET_WARNING_TIME,
SET_WARNING_BLOCKS;
SET_WARNING_BLOCKS
}
public enum CombatEventType {
ENTER_COMBAT,
END_COMBAT,
ENTITY_DIED;
ENTITY_DIED
}
public enum PlayerDigType {
@@ -151,7 +154,7 @@ public abstract class EnumWrappers {
DROP_ALL_ITEMS,
DROP_ITEM,
RELEASE_USE_ITEM,
SWAP_HELD_ITEMS;
SWAP_HELD_ITEMS
}
public enum PlayerAction {
@@ -163,12 +166,12 @@ public abstract class EnumWrappers {
START_RIDING_JUMP,
STOP_RIDING_JUMP,
OPEN_INVENTORY,
START_FALL_FLYING;
START_FALL_FLYING
}
public enum ScoreboardAction {
CHANGE,
REMOVE;
REMOVE
}
public enum Particle {
@@ -226,8 +229,8 @@ public abstract class EnumWrappers {
private static final Map<Integer, Particle> BY_ID;
static {
BY_ID = new HashMap<Integer, Particle>();
BY_NAME = new HashMap<String, Particle>();
BY_ID = new HashMap<>();
BY_NAME = new HashMap<>();
for (Particle particle : values()) {
BY_NAME.put(particle.getName().toLowerCase(Locale.ENGLISH), particle);
@@ -240,11 +243,11 @@ public abstract class EnumWrappers {
private final boolean longDistance;
private final int dataLength;
private Particle(String name, int id, boolean longDistance) {
Particle(String name, int id, boolean longDistance) {
this(name, id, longDistance, 0);
}
private Particle(String name, int id, boolean longDistance, int dataLength) {
Particle(String name, int id, boolean longDistance, int dataLength) {
this.name = name;
this.id = id;
this.longDistance = longDistance;
@@ -297,7 +300,8 @@ public abstract class EnumWrappers {
}
private final String key;
private SoundCategory(String key) {
SoundCategory(String key) {
this.key = key;
}
@@ -316,12 +320,12 @@ public abstract class EnumWrappers {
FEET,
LEGS,
CHEST,
HEAD;
HEAD
}
public enum Hand {
MAIN_HAND,
OFF_HAND;
OFF_HAND
}
public enum Direction {
@@ -330,22 +334,16 @@ public abstract class EnumWrappers {
NORTH,
SOUTH,
WEST,
EAST;
EAST
}
public enum ChatType {
CHAT(0),
SYSTEM(1),
GAME_INFO(2);
private byte id;
ChatType(int id) {
this.id = (byte) id;
}
CHAT,
SYSTEM,
GAME_INFO;
public byte getId() {
return id;
return (byte) ordinal();
}
}
@@ -434,6 +432,8 @@ public abstract class EnumWrappers {
if (nativeClass != null) {
FROM_NATIVE.put(nativeClass, converter);
FROM_WRAPPER.put(wrapperClass, converter);
} else if (ProtocolLogger.isDebugEnabled()) {
// new ClassNotFoundException(wrapperClass.getSimpleName()).printStackTrace();
}
}
@@ -447,7 +447,11 @@ public abstract class EnumWrappers {
try {
return FuzzyReflection.fromClass(clazz, true).getFieldListByType(Enum.class).get(index).getType();
} catch (Throwable ex) {
return null; // Unsupported in this version
/* if (ProtocolLogger.isDebugEnabled()) {
ex.printStackTrace();
} */
return null;
}
}
@@ -562,111 +566,121 @@ public abstract class EnumWrappers {
// Get the converters
public static EquivalentConverter<Protocol> getProtocolConverter() {
return new EnumConverter<Protocol>(Protocol.class);
return new EnumConverter<>(getProtocolClass(), Protocol.class);
}
public static EquivalentConverter<ClientCommand> getClientCommandConverter() {
return new EnumConverter<ClientCommand>(ClientCommand.class);
return new EnumConverter<>(getClientCommandClass(), ClientCommand.class);
}
public static EquivalentConverter<ChatVisibility> getChatVisibilityConverter() {
return new EnumConverter<ChatVisibility>(ChatVisibility.class);
return new EnumConverter<>(getChatVisibilityClass(), ChatVisibility.class);
}
public static EquivalentConverter<Difficulty> getDifficultyConverter() {
return new EnumConverter<Difficulty>(Difficulty.class);
return new EnumConverter<>(getDifficultyClass(), Difficulty.class);
}
public static EquivalentConverter<EntityUseAction> getEntityUseActionConverter() {
return new EnumConverter<EntityUseAction>(EntityUseAction.class);
return new EnumConverter<>(getEntityUseActionClass(), EntityUseAction.class);
}
public static EquivalentConverter<NativeGameMode> getGameModeConverter() {
return new EnumConverter<NativeGameMode>(NativeGameMode.class);
return new EnumConverter<>(getGameModeClass(), NativeGameMode.class);
}
public static EquivalentConverter<ResourcePackStatus> getResourcePackStatusConverter() {
return new EnumConverter<ResourcePackStatus>(ResourcePackStatus.class);
return new EnumConverter<>(getResourcePackStatusClass(), ResourcePackStatus.class);
}
public static EquivalentConverter<PlayerInfoAction> getPlayerInfoActionConverter() {
return new EnumConverter<PlayerInfoAction>(PlayerInfoAction.class);
return new EnumConverter<>(getPlayerInfoActionClass(), PlayerInfoAction.class);
}
public static EquivalentConverter<TitleAction> getTitleActionConverter() {
return new EnumConverter<TitleAction>(TitleAction.class);
return new EnumConverter<>(getTitleActionClass(), TitleAction.class);
}
public static EquivalentConverter<WorldBorderAction> getWorldBorderActionConverter() {
return new EnumConverter<WorldBorderAction>(WorldBorderAction.class);
return new EnumConverter<>(getWorldBorderActionClass(), WorldBorderAction.class);
}
public static EquivalentConverter<CombatEventType> getCombatEventTypeConverter() {
return new EnumConverter<CombatEventType>(CombatEventType.class);
return new EnumConverter<>(getCombatEventTypeClass(), CombatEventType.class);
}
public static EquivalentConverter<PlayerDigType> getPlayerDiggingActionConverter() {
return new EnumConverter<PlayerDigType>(PlayerDigType.class);
return new EnumConverter<>(getPlayerDigTypeClass(), PlayerDigType.class);
}
public static EquivalentConverter<PlayerAction> getEntityActionConverter() {
return new EnumConverter<PlayerAction>(PlayerAction.class);
return new EnumConverter<>(getPlayerActionClass(), PlayerAction.class);
}
public static EquivalentConverter<ScoreboardAction> getUpdateScoreActionConverter() {
return new EnumConverter<ScoreboardAction>(ScoreboardAction.class);
return new EnumConverter<>(getScoreboardActionClass(), ScoreboardAction.class);
}
public static EquivalentConverter<Particle> getParticleConverter() {
return new EnumConverter<Particle>(Particle.class);
return new EnumConverter<>(getParticleClass(), Particle.class);
}
public static EquivalentConverter<SoundCategory> getSoundCategoryConverter() {
return new EnumConverter<SoundCategory>(SoundCategory.class);
return new EnumConverter<>(getSoundCategoryClass(), SoundCategory.class);
}
public static EquivalentConverter<ItemSlot> getItemSlotConverter() {
return new EnumConverter<ItemSlot>(ItemSlot.class);
return new EnumConverter<>(getItemSlotClass(), ItemSlot.class);
}
public static EquivalentConverter<Hand> getHandConverter() {
return new EnumConverter<Hand>(Hand.class);
return new EnumConverter<>(getHandClass(), Hand.class);
}
public static EquivalentConverter<Direction> getDirectionConverter() {
return new EnumConverter<Direction>(Direction.class);
return new EnumConverter<>(getDirectionClass(), Direction.class);
}
public static EquivalentConverter<ChatType> getChatTypeConverter() {
return new EnumConverter<ChatType>(ChatType.class);
return new EnumConverter<>(getChatTypeClass(), ChatType.class);
}
/**
* Retrieve a generic enum converter for use with StructureModifiers.
* @param enumClass - Enum class
* @param genericClass - Generic nms enum class
* @param specificType - Specific enum class
* @return A generic enum converter
*/
public static <T extends Enum<T>> EquivalentConverter<T> getGenericConverter(Class<T> enumClass) {
return new EnumConverter<T>(enumClass);
public static <T extends Enum<T>> EquivalentConverter<T> getGenericConverter(Class<?> genericClass, Class<T> specificType) {
return new EnumConverter<>(genericClass, specificType);
}
/**
* @deprecated Replaced with {@link #getGenericConverter(Class, Class)}
*/
@Deprecated
public static <T extends Enum<T>> EquivalentConverter<T> getGenericConverter(Class<T> specificType) {
return new EnumConverter<>(null, specificType);
}
// The common enum converter
@SuppressWarnings({ "rawtypes", "unchecked" })
public static class EnumConverter<T extends Enum<T>> implements EquivalentConverter<T> {
private Class<?> genericType;
private Class<T> specificType;
public EnumConverter(Class<T> specificType) {
public EnumConverter(Class<?> genericType, Class<T> specificType) {
this.genericType = genericType;
this.specificType = specificType;
}
@Override
public T getSpecific(Object generic) {
// We know its an enum already!
return Enum.valueOf(specificType, ((Enum) generic).name());
}
@Override
public Object getGeneric(Class<?> genericType, T specific) {
public Object getGeneric(T specific) {
return Enum.valueOf((Class) genericType, specific.name());
}
@@ -674,5 +688,9 @@ public abstract class EnumWrappers {
public Class<T> getSpecificType() {
return specificType;
}
void setGenericType(Class<?> genericType) {
this.genericType = genericType;
}
}
}
@@ -122,7 +122,7 @@ public class MinecraftKey {
}
@Override
public Object getGeneric(Class<?> genericType, MinecraftKey specific) {
public Object getGeneric(MinecraftKey specific) {
if (constructor == null) {
try {
constructor = MinecraftReflection.getMinecraftKeyClass().getConstructor(String.class, String.class);
@@ -190,7 +190,7 @@ public class MultiBlockChangeInfo {
@Override
public MultiBlockChangeInfo getSpecific(Object generic) {
StructureModifier<Object> modifier = new StructureModifier<Object>(generic.getClass(), null, false).withTarget(generic);
StructureModifier<Object> modifier = new StructureModifier<>(generic.getClass(), null, false).withTarget(generic);
StructureModifier<Short> shorts = modifier.withType(short.class);
short location = shorts.read(0);
@@ -203,7 +203,7 @@ public class MultiBlockChangeInfo {
}
@Override
public Object getGeneric(Class<?> genericType, MultiBlockChangeInfo specific) {
public Object getGeneric(MultiBlockChangeInfo specific) {
try {
if (constructor == null) {
constructor = nmsClass.getConstructor(
@@ -216,7 +216,7 @@ public class MultiBlockChangeInfo {
return constructor.newInstance(
null,
specific.location,
BukkitConverters.getWrappedBlockDataConverter().getGeneric(MinecraftReflection.getIBlockDataClass(), specific.data)
BukkitConverters.getWrappedBlockDataConverter().getGeneric(specific.data)
);
} catch (Throwable ex) {
throw new RuntimeException("Failed to construct MultiBlockChangeInfo instance.", ex);
@@ -248,12 +248,12 @@ public class MultiBlockChangeInfo {
}
@Override
public Object getGeneric(Class<?> genericType, MultiBlockChangeInfo[] specific) {
public Object getGeneric(MultiBlockChangeInfo[] specific) {
Object[] result = (Object[]) Array.newInstance(nmsClass, specific.length);
// Wrap every item
for (int i = 0; i < result.length; i++) {
result[i] = converter.getGeneric(nmsClass, specific[i]);
result[i] = converter.getGeneric(specific[i]);
}
return result;
@@ -91,7 +91,7 @@ public class PlayerInfoData {
public static EquivalentConverter<PlayerInfoData> getConverter() {
return new EquivalentConverter<PlayerInfoData>() {
@Override
public Object getGeneric(Class<?> genericType, PlayerInfoData specific) {
public Object getGeneric(PlayerInfoData specific) {
if (constructor == null) {
try {
// public PlayerInfoData(Packet, GameProfile, int, GameMode, ChatComponent)
@@ -116,7 +116,7 @@ public class PlayerInfoData {
null,
specific.profile.handle,
specific.latency,
EnumWrappers.getGameModeConverter().getGeneric(EnumWrappers.getGameModeClass(), specific.gameMode),
EnumWrappers.getGameModeConverter().getGeneric(specific.gameMode),
specific.displayName != null ? specific.displayName.handle : null
);
return result;
@@ -128,7 +128,7 @@ public class PlayerInfoData {
@Override
public PlayerInfoData getSpecific(Object generic) {
if (MinecraftReflection.isPlayerInfoData(generic)) {
StructureModifier<Object> modifier = new StructureModifier<Object>(generic.getClass(), null, false)
StructureModifier<Object> modifier = new StructureModifier<>(generic.getClass(), null, false)
.withTarget(generic);
StructureModifier<WrappedGameProfile> gameProfiles = modifier.withType(
@@ -91,15 +91,14 @@ public class Vector3F {
}
public static EquivalentConverter<Vector3F> getConverter() {
return new IgnoreNullConverter<Vector3F>() {
return Converters.ignoreNull(new EquivalentConverter<Vector3F>() {
@Override
public Class<Vector3F> getSpecificType() {
return Vector3F.class;
}
@Override
protected Object getGenericValue(Class<?> genericType, Vector3F specific) {
public Object getGeneric(Vector3F specific) {
if (constructor == null) {
try {
constructor = clazz.getConstructor(float.class, float.class, float.class);
@@ -107,7 +106,7 @@ public class Vector3F {
throw new RuntimeException("Failed to find constructor for Vector3f", ex);
}
}
try {
return constructor.newInstance(specific.x, specific.y, specific.z);
} catch (ReflectiveOperationException ex) {
@@ -116,7 +115,7 @@ public class Vector3F {
}
@Override
protected Vector3F getSpecificValue(Object generic) {
public Vector3F getSpecific(Object generic) {
StructureModifier<Float> modifier = new StructureModifier<Float>(generic.getClass())
.withTarget(generic).withType(float.class);
float x = modifier.read(0);
@@ -124,6 +123,6 @@ public class Vector3F {
float z = modifier.read(2);
return new Vector3F(x, y, z);
}
};
});
}
}
@@ -2,7 +2,7 @@ package com.comphenix.protocol.wrappers;
import java.lang.reflect.Constructor;
import java.util.UUID;
import java.util.function.Supplier;
import javax.annotation.Nonnull;
import com.comphenix.protocol.reflect.FuzzyReflection;
@@ -91,7 +91,7 @@ public class WrappedAttributeModifier extends AbstractWrapper {
// Cached values
private final UUID uuid;
private final String name;
private final Supplier<String> name;
private final Operation operation;
private final double amount;
@@ -109,7 +109,7 @@ public class WrappedAttributeModifier extends AbstractWrapper {
// Use the supplied values instead of reading from the NMS instance
this.uuid = uuid;
this.name = name;
this.name = () -> name;
this.amount = amount;
this.operation = operation;
}
@@ -118,6 +118,7 @@ public class WrappedAttributeModifier extends AbstractWrapper {
* Construct an attribute modifier wrapper around a given NMS instance.
* @param handle - the NMS instance.
*/
@SuppressWarnings("unchecked")
protected WrappedAttributeModifier(@Nonnull Object handle) {
// Update handle and modifier
super(MinecraftReflection.getAttributeModifierClass());
@@ -126,7 +127,15 @@ public class WrappedAttributeModifier extends AbstractWrapper {
// Load final values, caching them
this.uuid = (UUID) modifier.withType(UUID.class).read(0);
this.name = (String) modifier.withType(String.class).read(0);
StructureModifier<String> stringMod = modifier.withType(String.class);
if (stringMod.size() == 0) {
Supplier<String> supplier = (Supplier<String>) modifier.withType(Supplier.class).read(0);
this.name = supplier;
} else {
this.name = () -> stringMod.read(0);
}
this.amount = (Double) modifier.withType(double.class).read(0);
this.operation = Operation.fromId((Integer) modifier.withType(int.class).read(0));
}
@@ -213,7 +222,7 @@ public class WrappedAttributeModifier extends AbstractWrapper {
* @return The attribute key.
*/
public String getName() {
return name;
return name.get();
}
/**
@@ -18,13 +18,15 @@ package com.comphenix.protocol.wrappers;
import java.lang.reflect.Modifier;
import org.bukkit.Material;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
/**
* Represents a wrapper around IBlockData.
@@ -32,39 +34,217 @@ import com.comphenix.protocol.utility.MinecraftReflection;
* @author dmulloy2
*/
public class WrappedBlockData extends AbstractWrapper {
public abstract class WrappedBlockData extends AbstractWrapper implements ClonableWrapper {
private static final Class<?> MAGIC_NUMBERS = MinecraftReflection.getCraftBukkitClass("util.CraftMagicNumbers");
private static final Class<?> IBLOCK_DATA = MinecraftReflection.getIBlockDataClass();
private static final Class<?> BLOCK = MinecraftReflection.getBlockClass();
private static MethodAccessor FROM_LEGACY_DATA = null;
private static MethodAccessor TO_LEGACY_DATA = null;
private static MethodAccessor GET_NMS_BLOCK = null;
private static MethodAccessor GET_BLOCK = null;
private static class NewBlockData extends WrappedBlockData {
private static MethodAccessor MATERIAL_FROM_BLOCK;
private static MethodAccessor TO_LEGACY_DATA;
private static MethodAccessor GET_BLOCK;
private static MethodAccessor BLOCK_FROM_MATERIAL;
private static MethodAccessor GET_BLOCK_DATA;
private static MethodAccessor FROM_LEGACY_DATA;
private static MethodAccessor GET_HANDLE;
static {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(BLOCK);
FuzzyMethodContract contract = FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.parameterExactArray(int.class)
.returnTypeExact(IBLOCK_DATA)
.build();
FROM_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
static {
if (MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE)) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MAGIC_NUMBERS);
FuzzyMethodContract contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(Material.class)
.parameterExactArray(BLOCK)
.build();
MATERIAL_FROM_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
contract = FuzzyMethodContract.newBuilder()
.banModifier(Modifier.STATIC)
.parameterExactArray(IBLOCK_DATA)
.returnTypeExact(int.class)
.build();
TO_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract, "toLegacyData"));
contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.parameterExactArray(Material.class)
.returnTypeExact(BLOCK)
.build();
BLOCK_FROM_MATERIAL = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
fuzzy = FuzzyReflection.fromClass(MAGIC_NUMBERS);
GET_NMS_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getBlock", BLOCK,
new Class<?>[] { Material.class }));
contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.parameterExactArray(IBLOCK_DATA)
.returnTypeExact(byte.class)
.build();
TO_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
fuzzy = FuzzyReflection.fromClass(IBLOCK_DATA);
GET_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getBlock", BLOCK,
new Class<?>[0]));
contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.parameterExactArray(Material.class, byte.class)
.returnTypeExact(IBLOCK_DATA)
.build();
FROM_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
fuzzy = FuzzyReflection.fromClass(IBLOCK_DATA);
contract = FuzzyMethodContract
.newBuilder()
.banModifier(Modifier.STATIC)
.returnTypeExact(BLOCK)
.parameterCount(0)
.build();
GET_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
fuzzy = FuzzyReflection.fromClass(BLOCK);
contract = FuzzyMethodContract
.newBuilder()
.banModifier(Modifier.STATIC)
.parameterCount(0)
.returnTypeExact(IBLOCK_DATA)
.build();
GET_BLOCK_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getCraftBukkitClass("block.data.CraftBlockData"));
contract = FuzzyMethodContract
.newBuilder()
.banModifier(Modifier.STATIC)
.parameterCount(0)
.returnTypeExact(IBLOCK_DATA)
.build();
GET_HANDLE = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
}
}
private NewBlockData(Object handle) {
super(handle);
}
@Override
public Material getType() {
Object block = GET_BLOCK.invoke(handle);
return (Material) MATERIAL_FROM_BLOCK.invoke(null, block);
}
@Override
public int getData() {
return ((Number) TO_LEGACY_DATA.invoke(null, handle)).intValue();
}
@Override
public void setType(Material material) {
Object block = BLOCK_FROM_MATERIAL.invoke(null, material);
setHandle(GET_BLOCK_DATA.invoke(block));
}
@Override
public void setData(int data) {
setTypeAndData(getType(), data);
}
@Override
public void setTypeAndData(Material material, int data) {
setHandle(TO_LEGACY_DATA.invoke(null, material, (byte) data));
}
@Override
public WrappedBlockData deepClone() {
return NewBlockData.createNewData(getType(), getData());
}
private static WrappedBlockData createNewData(Material material) {
Object block = BLOCK_FROM_MATERIAL.invoke(null, material);
return new NewBlockData(GET_BLOCK_DATA.invoke(block));
}
private static WrappedBlockData createNewData(Material material, int data) {
return new NewBlockData(FROM_LEGACY_DATA.invoke(null, material, (byte) data));
}
private static WrappedBlockData createNewData(BlockData data) {
return new NewBlockData(GET_HANDLE.invoke(data));
}
}
private static class OldBlockData extends WrappedBlockData {
private static MethodAccessor FROM_LEGACY_DATA;
private static MethodAccessor TO_LEGACY_DATA;
private static MethodAccessor GET_NMS_BLOCK;
private static MethodAccessor GET_BLOCK;
static {
if (!MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE)) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(BLOCK);
FuzzyMethodContract contract = FuzzyMethodContract
.newBuilder()
.banModifier(Modifier.STATIC)
.parameterExactArray(int.class)
.returnTypeExact(IBLOCK_DATA)
.build();
FROM_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
contract = FuzzyMethodContract
.newBuilder()
.banModifier(Modifier.STATIC)
.parameterExactArray(IBLOCK_DATA)
.returnTypeExact(int.class)
.build();
TO_LEGACY_DATA = Accessors.getMethodAccessor(fuzzy.getMethod(contract, "toLegacyData"));
fuzzy = FuzzyReflection.fromClass(MAGIC_NUMBERS);
GET_NMS_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getBlock", BLOCK,
new Class<?>[]{Material.class}));
fuzzy = FuzzyReflection.fromClass(IBLOCK_DATA);
GET_BLOCK = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getBlock", BLOCK,
new Class<?>[0]));
}
}
private OldBlockData(Object handle) {
super(handle);
}
@Override
public Material getType() {
Object block = GET_BLOCK.invoke(handle);
return BukkitConverters.getBlockConverter().getSpecific(block);
}
@Override
public int getData() {
Object block = GET_BLOCK.invoke(handle);
return (Integer) TO_LEGACY_DATA.invoke(block, handle);
}
@Override
public void setType(Material type) {
setTypeAndData(type, 0);
}
@Override
public void setData(int data) {
setTypeAndData(getType(), data);
}
@Override
public void setTypeAndData(Material type, int data) {
Object nmsBlock = GET_NMS_BLOCK.invoke(null, type);
Object blockData = FROM_LEGACY_DATA.invoke(nmsBlock, data);
setHandle(blockData);
}
@Override
public WrappedBlockData deepClone() {
return WrappedBlockData.createData(getType(), getData());
}
private static WrappedBlockData createOldData(Material type) {
Object blockData = GET_BLOCK.invoke(null, type);
return new OldBlockData(blockData);
}
private static WrappedBlockData createOldData(Material type, int data) {
Object nmsBlock = GET_NMS_BLOCK.invoke(null, type);
Object blockData = FROM_LEGACY_DATA.invoke(nmsBlock, data);
return new OldBlockData(blockData);
}
}
public WrappedBlockData(Object handle) {
@@ -73,60 +253,43 @@ public class WrappedBlockData extends AbstractWrapper {
}
/**
* Retrieves the type of this BlockData.
* @return The type of this BlockData.
* Gets this BlockData's Bukkit material
* @return The Bukkit material
*/
public Material getType() {
Object block = GET_BLOCK.invoke(handle);
return BukkitConverters.getBlockConverter().getSpecific(block);
}
public abstract Material getType();
/**
* Retrieves the type id of this BlockData.
* @return The type id of this BlockData.
* @deprecated ID's are deprecated
* Gets this BlockData's legacy data
* @return The legacy data
* @deprecated By the flattening in 1.13. Fine on lower versions.
*/
@Deprecated
public int getTypeId() {
Object block = GET_BLOCK.invoke(handle);
return BukkitConverters.getBlockIDConverter().getSpecific(block);
}
public abstract int getData();
/**
* Retrieves the data of this BlockData.
* @return The data of this BlockData.
* Sets this BlockData's type
* @param material Bukkit material
*/
public int getData() {
Object block = GET_BLOCK.invoke(handle);
return (Integer) TO_LEGACY_DATA.invoke(block, handle);
}
public abstract void setType(Material material);
/**
* Sets the type of this BlockData.
* @param type New type
* Sets this BlockData's legacy data
* @param data The new legacy data
* @deprecated By the flattening in 1.13. Fine on lower versions.
*/
public void setType(Material type) {
setTypeAndData(type, 0);
}
@Deprecated
public abstract void setData(int data);
/**
* Sets the data of this BlockData.
* @param data New data
* Sets this BlockData's type and legacy data
* @param material The new Bukkit material
* @param data The new legacy data
* @deprecated By the flattening in 1.13. Fine on lower versions.
*/
public void setData(int data) {
setTypeAndData(getType(), data);
}
@Deprecated
public abstract void setTypeAndData(Material material, int data);
/**
* Sets the type and data of this BlockData.
* @param type New type
* @param data New data
*/
public void setTypeAndData(Material type, int data) {
Object nmsBlock = GET_NMS_BLOCK.invoke(null, type);
Object blockData = FROM_LEGACY_DATA.invoke(nmsBlock, data);
setHandle(blockData);
}
public abstract WrappedBlockData deepClone();
/**
* Creates a new BlockData instance with the given type and no data.
@@ -134,7 +297,8 @@ public class WrappedBlockData extends AbstractWrapper {
* @return New BlockData
*/
public static WrappedBlockData createData(Material type) {
return createData(type, 0);
return MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE) ? NewBlockData.createNewData(type)
: OldBlockData.createOldData(type);
}
/**
@@ -142,19 +306,26 @@ public class WrappedBlockData extends AbstractWrapper {
* @param type Block type
* @param data Block data
* @return New BlockData
* @deprecated The flattening
*/
@Deprecated
public static WrappedBlockData createData(Material type, int data) {
Object nmsBlock = GET_NMS_BLOCK.invoke(null, type);
Object blockData = FROM_LEGACY_DATA.invoke(nmsBlock, data);
return new WrappedBlockData(blockData);
return MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE) ? NewBlockData.createNewData(type, data)
: OldBlockData.createOldData(type, data);
}
public static WrappedBlockData fromHandle(Object handle) {
return MinecraftVersion.atOrAbove(MinecraftVersion.AQUATIC_UPDATE) ? new NewBlockData(handle)
: new OldBlockData(handle);
}
/**
* Retrieve a deep copy of the current wrapper object.
* @return The cloned object.
* Creates a new Wrapped Block Data instance from a given Spigot Block Data
* @param data Spigot block data
* @return The new Wrapped Block Data
*/
public WrappedBlockData deepClone() {
return WrappedBlockData.createData(getType(), getData());
public static WrappedBlockData createData(BlockData data) {
return NewBlockData.createNewData(data);
}
@Override
@@ -173,11 +344,14 @@ public class WrappedBlockData extends AbstractWrapper {
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (o instanceof WrappedBlockData) {
WrappedBlockData that = (WrappedBlockData) o;
return this.getType() == that.getType() && getData() == that.getData();
return this.handle.equals(that.handle)
|| (this.getType() == that.getType() && this.getData() == that.getData());
}
return false;
}
}
}
@@ -1,6 +1,6 @@
/**
* ProtocolLib - Bukkit server library that allows access to the Minecraft protocol.
* Copyright (C) 2016 dmulloy2
* Copyright (C) 2018 dmulloy2
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 2 of
@@ -39,11 +39,10 @@ import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
/**
* Represents a DataWatcher in 1.8 thru 1.10
*
* Represents a DataWatcher
* @author dmulloy2
*/
public class WrappedDataWatcher extends AbstractWrapper implements Iterable<WrappedWatchableObject> {
public class WrappedDataWatcher extends AbstractWrapper implements Iterable<WrappedWatchableObject>, ClonableWrapper {
private static final Class<?> HANDLE_TYPE = MinecraftReflection.getDataWatcherClass();
private static MethodAccessor GETTER = null;
@@ -55,7 +54,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
private static FieldAccessor MAP_FIELD = null;
private static ConstructorAccessor constructor = null;
private static ConstructorAccessor lightningConstructor = null;
private static ConstructorAccessor eggConstructor = null;
private static Object fakeEntity = null;
@@ -74,7 +73,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
}
/**
* Constructs a new DataWatcher using a fake lightning entity. The
* Constructs a new DataWatcher using a fake egg entity. The
* resulting DataWatcher will not have any keys or values and new ones will
* have to be added using watcher objects.
*/
@@ -94,7 +93,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
}
/**
* Constructs a new DataWatcher using a fake lightning entity and a given
* Constructs a new DataWatcher using a fake egg entity and a given
* list of watchable objects.
*
* @param objects The list of objects
@@ -126,13 +125,14 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
return fakeEntity;
}
// We can create a fake lightning strike without it affecting anything
if (lightningConstructor == null) {
lightningConstructor = Accessors.getConstructorAccessor(MinecraftReflection.getMinecraftClass("EntityLightning"),
MinecraftReflection.getNmsWorldClass(), double.class, double.class, double.class, boolean.class);
// We can create a fake egg without it affecting anything
// Mojang added difficulty to lightning strikes, so this'll have to do
if (eggConstructor == null) {
eggConstructor = Accessors.getConstructorAccessor(MinecraftReflection.getMinecraftClass("EntityEgg"),
MinecraftReflection.getNmsWorldClass(), double.class, double.class, double.class);
}
return fakeEntity = lightningConstructor.invoke(null, 0, 0, 0, true);
return fakeEntity = eggConstructor.invoke(null, 0, 0, 0);
}
// ---- Collection Methods
@@ -140,15 +140,16 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
@SuppressWarnings("unchecked")
private Map<Integer, Object> getMap() {
if (MAP_FIELD == null) {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
MAP_FIELD = Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract.newBuilder()
.banModifier(Modifier.STATIC)
.typeDerivedOf(Map.class)
.build()));
}
if (MAP_FIELD == null) {
throw new FieldAccessException("Could not find index <-> Item map.");
try {
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handleType, true);
MAP_FIELD = Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract
.newBuilder()
.banModifier(Modifier.STATIC)
.typeDerivedOf(Map.class)
.build()));
} catch (IllegalArgumentException ex) {
throw new FieldAccessException("Failed to find watchable object map");
}
}
return (Map<Integer, Object>) MAP_FIELD.get(handle);
@@ -371,7 +372,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
try {
Object value = GETTER.invoke(handle, object.getHandle());
return WrappedWatchableObject.getWrapped(value);
} catch (RuntimeException ex) {
} catch (Exception ex) {
// Nothing exists at this index
return null;
}
@@ -677,7 +678,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
*/
public WrappedDataWatcherObject(Object handle) {
this.handle = handle;
this.modifier = new StructureModifier<Object>(HANDLE_TYPE).withTarget(handle);
this.modifier = new StructureModifier<>(HANDLE_TYPE).withTarget(handle);
}
/**
@@ -692,18 +693,31 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
static WrappedDataWatcherObject fromIndex(int index) {
if (MinecraftReflection.watcherObjectExists()) {
return new WrappedDataWatcherObject(index, null);
return new WrappedDataWatcherObject(newHandle(index));
} else {
return new DummyWatcherObject(index);
}
}
private static Object newHandle(int index, Serializer serializer) {
private static Object newHandle(int index) {
Validate.isTrue(index >= 0, "index cannot be negative!");
if (constructor == null) {
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE.getConstructors()[0]);
}
Object handle = serializer != null ? serializer.getHandle() : null;
return constructor.invoke(index, null);
}
private static Object newHandle(int index, Serializer serializer) {
Validate.isTrue(index >= 0, "index cannot be negative!");
Validate.notNull(serializer, "serializer cannot be null!");
if (constructor == null) {
constructor = Accessors.getConstructorAccessor(HANDLE_TYPE.getConstructors()[0]);
}
Object handle = serializer.getHandle();
return constructor.invoke(index, handle);
}
@@ -914,7 +928,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @return The serializer, or null if none exists
*/
public static Serializer get(Class<?> clazz) {
Validate.notNull("Class cannot be null!");
Validate.notNull(clazz,"Class cannot be null!");
initialize();
for (Serializer serializer : REGISTRY) {
@@ -923,7 +937,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
}
}
return null;
throw new IllegalArgumentException("No serializer found for " + clazz);
}
/**
@@ -940,6 +954,8 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
Validate.notNull(clazz, "Class cannot be null!");
initialize();
Validate.notEmpty(REGISTRY, "Registry has no elements!");
for (Serializer serializer : REGISTRY) {
if (serializer.getType().equals(clazz)
&& serializer.isOptional() == optional) {
@@ -947,7 +963,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
}
}
return null;
throw new IllegalArgumentException("No serializer found for " + (optional ? "Optional<" + clazz + ">" : clazz));
}
/**
@@ -956,7 +972,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @return The serializer, or null if none exists
*/
public static Serializer fromHandle(Object handle) {
Validate.notNull("handle cannot be null!");
Validate.notNull(handle, "handle cannot be null!");
initialize();
for (Serializer serializer : REGISTRY) {
@@ -984,7 +1000,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
Type[] args = type.getActualTypeArguments();
Type arg = args[0];
Class<?> innerClass = null;
Class<?> innerClass;
boolean optional = false;
if (arg instanceof Class<?>) {
@@ -1004,6 +1020,10 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
throw new IllegalStateException("Failed to read field " + candidate);
}
if (serializer == null) {
throw new RuntimeException("Failed to read serializer: " + candidate.getName());
}
REGISTRY.add(new Serializer(innerClass, serializer, optional));
}
}
@@ -1016,7 +1036,16 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @return The serializer
*/
public static Serializer getChatComponentSerializer() {
return get(MinecraftReflection.getIChatBaseComponentClass());
return getChatComponentSerializer(false);
}
/**
* Gets the serializer for IChatBaseComponents
* @param optional If true, objects <b>must</b> be wrapped in an {@link Optional}
* @return The serializer
*/
public static Serializer getChatComponentSerializer(boolean optional) {
return get(MinecraftReflection.getIChatBaseComponentClass(), optional);
}
/**
@@ -0,0 +1,153 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Modifier;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract;
import com.comphenix.protocol.utility.MinecraftReflection;
import org.bukkit.Color;
import org.bukkit.Particle;
import org.bukkit.inventory.ItemStack;
/**
* Represents an immutable wrapped ParticleParam in 1.13
*/
public class WrappedParticle<T> {
private static MethodAccessor toBukkit;
private static MethodAccessor toNMS;
private static MethodAccessor toCraftData;
private static void ensureMethods() {
if (toBukkit != null && toNMS != null) {
return;
}
FuzzyReflection fuzzy = FuzzyReflection.fromClass(MinecraftReflection.getCraftBukkitClass("CraftParticle"));
FuzzyMethodContract contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(Particle.class)
.parameterExactType(MinecraftReflection.getMinecraftClass("ParticleParam"))
.build();
toBukkit = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(MinecraftReflection.getMinecraftClass("ParticleParam"))
.parameterCount(2)
.build();
toNMS = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
Class<?> cbData = MinecraftReflection.getCraftBukkitClass("block.data.CraftBlockData");
fuzzy = FuzzyReflection.fromClass(cbData);
contract = FuzzyMethodContract
.newBuilder()
.requireModifier(Modifier.STATIC)
.returnTypeExact(cbData)
.parameterExactArray(MinecraftReflection.getIBlockDataClass())
.build();
toCraftData = Accessors.getMethodAccessor(fuzzy.getMethod(contract));
}
private final Particle particle;
private final T data;
private final Object handle;
private WrappedParticle(Object handle, Particle particle, T data) {
this.handle = handle;
this.particle = particle;
this.data = data;
}
/**
* @return This particle's Bukkit type
*/
public Particle getParticle() {
return particle;
}
/**
* Gets this Particle's Bukkit/ProtocolLib data. The type of this data depends on the
* {@link #getParticle() Particle type}. For Block particles it will be {@link WrappedBlockData},
* for Item crack particles, it will be an {@link ItemStack}, and for redstone particles it will
* be {@link Particle.DustOptions}
*
* @return The particle data
*/
public T getData() {
return data;
}
/**
* @return NMS handle
*/
public Object getHandle() {
return handle;
}
public static WrappedParticle fromHandle(Object handle) {
ensureMethods();
Particle bukkit = (Particle) toBukkit.invoke(null, handle);
Object data = null;
switch (bukkit) {
case BLOCK_CRACK:
case BLOCK_DUST:
case FALLING_DUST:
data = getBlockData(handle);
break;
case ITEM_CRACK:
data = getItem(handle);
break;
case REDSTONE:
data = getRedstone(handle);
break;
default:
break;
}
return new WrappedParticle<>(handle, bukkit, data);
}
private static WrappedBlockData getBlockData(Object handle) {
return new StructureModifier<>(handle.getClass())
.withTarget(handle)
.withType(MinecraftReflection.getIBlockDataClass(), BukkitConverters.getWrappedBlockDataConverter())
.read(0);
}
private static Object getItem(Object handle) {
return new StructureModifier<>(handle.getClass())
.withTarget(handle)
.withType(MinecraftReflection.getItemStackClass(), BukkitConverters.getItemStackConverter())
.read(0);
}
private static Object getRedstone(Object handle) {
StructureModifier<Float> modifier = new StructureModifier<>(handle.getClass()).withTarget(handle).withType(float.class);
return new Particle.DustOptions(Color.fromRGB(
(int) (modifier.read(0) * 255),
(int) (modifier.read(1) * 255),
(int) (modifier.read(2) * 255)),
modifier.read(3));
}
public static <T> WrappedParticle<T> create(Particle particle, T data) {
ensureMethods();
Object bukkitData = data;
if (data instanceof WrappedBlockData) {
WrappedBlockData blockData = (WrappedBlockData) data;
bukkitData = toCraftData.invoke(null, blockData.getHandle());
}
Object handle = toNMS.invoke(null, particle, bukkitData);
return new WrappedParticle<>(handle, particle, data);
}
}
@@ -40,7 +40,7 @@ import io.netty.handler.codec.base64.Base64;
* Represents a server ping packet data.
* @author Kristian
*/
public class WrappedServerPing extends AbstractWrapper {
public class WrappedServerPing extends AbstractWrapper implements ClonableWrapper {
private static Class<?> GAME_PROFILE = MinecraftReflection.getGameProfileClass();
private static Class<?> GAME_PROFILE_ARRAY = MinecraftReflection.getArrayClass(GAME_PROFILE);
@@ -280,7 +280,7 @@ public class WrappedServerPing extends AbstractWrapper {
public void setPlayers(Iterable<? extends WrappedGameProfile> profile) {
if (players == null)
resetPlayers();
PLAYERS_PROFILES.set(players, (profile != null) ? PROFILE_CONVERT.getGeneric(GAME_PROFILE_ARRAY, profile) : null);
PLAYERS_PROFILES.set(players, (profile != null) ? PROFILE_CONVERT.getGeneric(profile) : null);
}
/**
@@ -273,22 +273,22 @@ public class WrappedWatchableObject extends AbstractWrapper {
if (wrapped instanceof WrappedChatComponent) {
return ((WrappedChatComponent) wrapped).getHandle();
} else if (wrapped instanceof ItemStack) {
return BukkitConverters.getItemStackConverter().getGeneric(MinecraftReflection.getItemStackClass(), (ItemStack) wrapped);
return BukkitConverters.getItemStackConverter().getGeneric((ItemStack) wrapped);
} else if (wrapped instanceof WrappedBlockData) {
return BukkitConverters.getWrappedBlockDataConverter().getGeneric(MinecraftReflection.getIBlockDataClass(), (WrappedBlockData) wrapped);
return BukkitConverters.getWrappedBlockDataConverter().getGeneric((WrappedBlockData) wrapped);
} else if (wrapped instanceof Vector3F) {
return Vector3F.getConverter().getGeneric(Vector3F.getMinecraftClass(), (Vector3F) wrapped);
return Vector3F.getConverter().getGeneric((Vector3F) wrapped);
} else if (wrapped instanceof BlockPosition) {
return BlockPosition.getConverter().getGeneric(MinecraftReflection.getBlockPositionClass(), (BlockPosition) wrapped);
return BlockPosition.getConverter().getGeneric((BlockPosition) wrapped);
} else if (wrapped instanceof Direction) {
return EnumWrappers.getDirectionConverter().getGeneric(EnumWrappers.getDirectionClass(), (Direction) wrapped);
return EnumWrappers.getDirectionConverter().getGeneric((Direction) wrapped);
} else if (wrapped instanceof NbtCompound) {
return NbtFactory.fromBase((NbtCompound) wrapped).getHandle();
}
// Legacy classes
if (wrapped instanceof ChunkPosition) {
return ChunkPosition.getConverter().getGeneric(MinecraftReflection.getChunkPositionClass(), (ChunkPosition) wrapped);
return ChunkPosition.getConverter().getGeneric((ChunkPosition) wrapped);
} else if (wrapped instanceof WrappedChunkCoordinate) {
return ((WrappedChunkCoordinate) wrapped).getHandle();
}
@@ -18,6 +18,8 @@
package com.comphenix.protocol.wrappers.nbt;
import com.comphenix.protocol.wrappers.ClonableWrapper;
/**
* Represents a generic container for an NBT element.
* <p>
@@ -26,7 +28,7 @@ package com.comphenix.protocol.wrappers.nbt;
* @author Kristian
* @param <TType> - type of the value that is stored.
*/
public interface NbtBase<TType> {
public interface NbtBase<TType> extends ClonableWrapper {
/**
* Accepts a NBT visitor.
* @param visitor - the hierarchical NBT visitor.
@@ -78,10 +80,14 @@ public interface NbtBase<TType> {
* @param newValue - the new value of this tag.
*/
public abstract void setValue(TType newValue);
/**
* Clone the current NBT tag.
* @return The cloned tag.
*/
public abstract NbtBase<TType> deepClone();
default Object getHandle() {
return null;
}
}
@@ -26,6 +26,7 @@ import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
@@ -168,6 +169,31 @@ public class NbtFactory {
}
return fromBase(result);
}
/**
* Constructs a wrapper for a NBT tag in an ItemStack. This is where auxillary
* data such as enchantments, name, and lore is stored. It doesn't include the material,
* damage value, or stack size.
* <p>
* This differs from {@link #fromItemTag(ItemStack)} in that the tag is not created if it
* doesn't already exist.
*
* @param stack the ItemStack. Must be a CraftItemStack. Use {@link MinecraftReflection#getBukkitItemStack(Object)}
* @return A wrapper for the NBT tag if it exists, an empty Optional if not
*/
public static Optional<NbtWrapper<?>> fromItemOptional(ItemStack stack) {
checkItemStack(stack);
StructureModifier<NbtBase<?>> modifier = getStackModifier(stack);
NbtBase<?> result = modifier.read(0);
// Create the tag if it doesn't exist
if (result == null) {
return Optional.empty();
}
return Optional.of(fromBase(result));
}
/**
* Load a NBT compound from a GZIP compressed file.
@@ -19,6 +19,8 @@ package com.comphenix.protocol.wrappers.nbt;
import java.io.DataOutput;
import com.comphenix.protocol.wrappers.ClonableWrapper;
/**
* Indicates that this NBT wraps an underlying net.minecraft.server instance.
* <p>
@@ -28,7 +30,7 @@ import java.io.DataOutput;
*
* @param <TType> - type of the value that is stored.
*/
public interface NbtWrapper<TType> extends NbtBase<TType> {
public interface NbtWrapper<TType> extends NbtBase<TType>, ClonableWrapper {
/**
* Retrieve the underlying net.minecraft.server instance.
* @return The NMS instance.
@@ -4,25 +4,24 @@ import java.io.IOException;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentMap;
import net.sf.cglib.asm.ClassReader;
import net.sf.cglib.asm.MethodVisitor;
import net.sf.cglib.asm.Opcodes;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.EnhancerFactory;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.Maps;
import net.sf.cglib.asm.$ClassReader;
import net.sf.cglib.asm.$ClassVisitor;
import net.sf.cglib.asm.$MethodVisitor;
import net.sf.cglib.asm.$Opcodes;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.bukkit.block.BlockState;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.reflect.compiler.EmptyClassVisitor;
import com.comphenix.protocol.reflect.compiler.EmptyMethodVisitor;
import com.comphenix.protocol.utility.EnhancerFactory;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.google.common.collect.Maps;
/**
* Manipulate tile entities.
* @author Kristian
@@ -90,26 +89,26 @@ class TileEntityAccessor<T extends BlockState> {
private void findMethodsUsingASM() throws IOException {
final Class<?> nbtCompoundClass = MinecraftReflection.getNBTCompoundClass();
final Class<?> tileEntityClass = MinecraftReflection.getTileEntityClass();
final ClassReader reader = new ClassReader(tileEntityClass.getCanonicalName());
final $ClassReader reader = new $ClassReader(tileEntityClass.getCanonicalName());
final String tagCompoundName = getJarName(MinecraftReflection.getNBTCompoundClass());
final String expectedDesc = "(L" + tagCompoundName + ";)";
reader.accept(new EmptyClassVisitor() {
reader.accept(new $ClassVisitor($Opcodes.ASM5) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
public $MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
final String methodName = name;
// Detect read/write calls to NBTTagCompound
if (desc.startsWith(expectedDesc)) {
return new EmptyMethodVisitor() {
return new $MethodVisitor($Opcodes.ASM5) {
private int readMethods;
private int writeMethods;
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean intf) {
// This must be a virtual call on NBTTagCompound that accepts a String
if (opcode == Opcodes.INVOKEVIRTUAL
if (opcode == $Opcodes.INVOKEVIRTUAL
&& tagCompoundName.equals(owner)
&& desc.startsWith("(Ljava/lang/String")) {
+14 -12
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>ProtocolLib</artifactId>
<name>ProtocolLib</name>
<version>${minorVersion}</version>
<version>4.4.0</version>
<description>Provides read/write access to the Minecraft protocol.</description>
<url>http://www.spigotmc.org/resources/protocollib.1997/</url>
@@ -12,7 +12,7 @@
<parent>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib-Parent</artifactId>
<version>v4-SNAPSHOT</version>
<version>4.4.0-SNAPSHOT</version>
<relativePath>../../</relativePath>
</parent>
@@ -20,6 +20,8 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.number></project.build.number>
<project.fullVersion>${project.version}</project.fullVersion>
<powermock.version>1.7.4</powermock.version>
</properties>
<build>
@@ -40,7 +42,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<version>3.1.1</version>
<executions>
<execution>
<phase>package</phase>
@@ -73,7 +75,7 @@
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
@@ -83,7 +85,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<version>3.1.0</version>
<configuration>
<archive>
<manifestEntries>
@@ -100,7 +102,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.10</version>
<version>2.22.0</version>
<configuration>
<systemProperties>
<property>
@@ -238,25 +240,25 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.4</version>
<artifactId>mockito-core</artifactId>
<version>2.21.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.5</version>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.5</version>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -654,4 +654,8 @@ public class ProtocolLib extends JavaPlugin {
public Statistics getStatistics() {
return statistics;
}
public ProtocolConfig getProtocolConfig() {
return config;
}
}
@@ -16,22 +16,12 @@
*/
package com.comphenix.protocol.injector.netty;
import java.lang.reflect.InvocationTargetException;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.ListIterator;
import java.util.*;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentMap;
import net.sf.cglib.proxy.Factory;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.concurrent.atomic.AtomicInteger;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Protocol;
@@ -54,32 +44,30 @@ import com.comphenix.protocol.utility.MinecraftProtocolVersion;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.google.common.base.Preconditions;
import com.google.common.collect.MapMaker;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.internal.TypeParameterMatcher;
import net.sf.cglib.proxy.Factory;
import org.apache.commons.lang3.Validate;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
/**
* Represents a channel injector.
* @author Kristian
*/
public class ChannelInjector extends ByteToMessageDecoder implements Injector {
public static final ReportType REPORT_CANNOT_INTERCEPT_SERVER_PACKET = new ReportType("Unable to intercept a written server packet.");
public static final ReportType REPORT_CANNOT_INTERCEPT_CLIENT_PACKET = new ReportType("Unable to intercept a read client packet.");
public static final ReportType REPORT_CANNOT_EXECUTE_IN_CHANNEL_THREAD = new ReportType("Cannot execute code in channel thread.");
public static final ReportType REPORT_CANNOT_FIND_GET_VERSION = new ReportType("Cannot find getVersion() in NetworkMananger");
public static final ReportType REPORT_CANNOT_SEND_PACKET = new ReportType("Unable to send packet %s to %s");
private static final ReportType REPORT_CANNOT_INTERCEPT_SERVER_PACKET = new ReportType("Unable to intercept a written server packet.");
private static final ReportType REPORT_CANNOT_INTERCEPT_CLIENT_PACKET = new ReportType("Unable to intercept a read client packet.");
private static final ReportType REPORT_CANNOT_EXECUTE_IN_CHANNEL_THREAD = new ReportType("Cannot execute code in channel thread.");
private static final ReportType REPORT_CANNOT_SEND_PACKET = new ReportType("Unable to send packet %s to %s");
/**
* Indicates that a packet has bypassed packet listeners.
@@ -92,7 +80,13 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
// Versioning
private static Class<?> PACKET_SET_PROTOCOL = null;
private static AttributeKey<Integer> PROTOCOL_KEY = AttributeKey.valueOf("PROTOCOL");
private static AtomicInteger keyId = new AtomicInteger();
private static AttributeKey<Integer> PROTOCOL_KEY;
static {
PROTOCOL_KEY = AttributeKey.valueOf("PROTOCOL-" + keyId.getAndIncrement());
}
// Saved accessors
private static MethodAccessor DECODE_BUFFER;
@@ -119,7 +113,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
private VolatileField channelField;
// Known network markers
private ConcurrentMap<Object, NetworkMarker> packetMarker = new MapMaker().weakKeys().makeMap();
private Map<Object, NetworkMarker> packetMarker = new WeakHashMap<>();
/**
* Indicate that this packet has been processed by event listeners.
@@ -136,18 +130,13 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
/**
* A flag set by the main thread to indiciate that a packet should not be processed.
*/
private final ThreadLocal<Boolean> scheduleProcessPackets = new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
return true;
};
};
private final ThreadLocal<Boolean> scheduleProcessPackets = ThreadLocal.withInitial(() -> true);
// Other handlers
private ByteToMessageDecoder vanillaDecoder;
private MessageToByteEncoder<Object> vanillaEncoder;
private Deque<PacketEvent> finishQueue = new ArrayDeque<PacketEvent>();
private Deque<PacketEvent> finishQueue = new ArrayDeque<>();
// The channel listener
private ChannelListener channelListener;
@@ -167,7 +156,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
* @param channelListener - a listener.
* @param factory - the factory that created this injector
*/
public ChannelInjector(Player player, Object networkManager, Channel channel, ChannelListener channelListener, InjectionFactory factory) {
ChannelInjector(Player player, Object networkManager, Channel channel, ChannelListener channelListener, InjectionFactory factory) {
this.player = Preconditions.checkNotNull(player, "player cannot be NULL");
this.networkManager = Preconditions.checkNotNull(networkManager, "networkMananger cannot be NULL");
this.originalChannel = Preconditions.checkNotNull(channel, "channel cannot be NULL");
@@ -176,8 +165,9 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
this.processor = new NetworkProcessor(ProtocolLibrary.getErrorReporter());
// Get the channel field
this.channelField = new VolatileField(FuzzyReflection.fromObject(networkManager, true).getFieldByType("channel", Channel.class),
networkManager, true);
this.channelField = new VolatileField(FuzzyReflection
.fromObject(networkManager, true)
.getFieldByType("channel", Channel.class), networkManager, true);
}
/**
@@ -205,12 +195,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
// pipeline with only some of the handlers removed
if (Bukkit.isPrimaryThread()) {
// Just like in the close() method, we'll avoid blocking the main thread
executeInChannelThread(new Runnable() {
@Override
public void run() {
inject();
}
});
executeInChannelThread(this::inject);
return false; // We don't know
}
@@ -224,9 +209,9 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
vanillaEncoder = (MessageToByteEncoder<Object>) originalChannel.pipeline().get("encoder");
if (vanillaDecoder == null)
throw new IllegalArgumentException("Unable to find vanilla decoder in " + originalChannel.pipeline() );
throw new IllegalArgumentException("Unable to find vanilla decoder in " + originalChannel.pipeline());
if (vanillaEncoder == null)
throw new IllegalArgumentException("Unable to find vanilla encoder in " + originalChannel.pipeline() );
throw new IllegalArgumentException("Unable to find vanilla encoder in " + originalChannel.pipeline());
patchEncoder(vanillaEncoder);
if (DECODE_BUFFER == null)
@@ -239,7 +224,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
// Intercept sent packets
MessageToByteEncoder<Object> protocolEncoder = new MessageToByteEncoder<Object>() {
@Override
protected void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) throws Exception {
protected void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) {
if (packet instanceof WirePacket) {
// Special case for wire format
ChannelInjector.this.encodeWirePacket((WirePacket) packet, output);
@@ -251,17 +236,17 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
@Override
public void write(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) throws Exception {
super.write(ctx, packet, promise);
ChannelInjector.this.finalWrite(ctx, packet, promise);
ChannelInjector.this.finalWrite();
}
};
// Intercept recieved packets
ChannelInboundHandlerAdapter finishHandler = new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// Execute context first
ctx.fireChannelRead(msg);
ChannelInjector.this.finishRead(ctx, msg);
ChannelInjector.this.finishRead();
}
};
@@ -301,17 +286,14 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
if (event != null && event.isCancelled())
return null;
return new Callable<T>() {
@Override
public T call() throws Exception {
T result = null;
return () -> {
T result;
// This field must only be updated in the pipeline thread
currentEvent = event;
result = callable.call();
currentEvent = null;
return result;
}
// This field must only be updated in the pipeline thread
currentEvent = event;
result = callable.call();
currentEvent = null;
return result;
};
}
@@ -323,17 +305,14 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
if (event != null && event.isCancelled())
return null;
return new Runnable() {
@Override
public void run() {
currentEvent = event;
runnable.run();
currentEvent = null;
}
return () -> {
currentEvent = event;
runnable.run();
currentEvent = null;
};
}
protected PacketEvent handleScheduled(Object instance, FieldAccessor accessor) {
PacketEvent handleScheduled(Object instance, FieldAccessor accessor) {
// Let the filters handle this packet
Object original = accessor.get(instance);
@@ -349,15 +328,17 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
return BYPASSED_PACKET;
}
}
PacketEvent event = processSending(original);
PacketEvent event = processSending(original);
if (event != null && !event.isCancelled()) {
Object changed = event.getPacket().getHandle();
// Change packet to be scheduled
if (original != changed)
if (original != changed) {
accessor.set(instance, changed);
};
}
}
return event != null ? event : BYPASSED_PACKET;
}
});
@@ -394,17 +375,20 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
if (ENCODER_TYPE_MATCHER == null) {
ENCODER_TYPE_MATCHER = Accessors.getFieldAccessor(encoder.getClass(), "matcher", true);
}
ENCODER_TYPE_MATCHER.set(encoder, TypeParameterMatcher.get(MinecraftReflection.getPacketClass()));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (channelListener.isDebug())
if (channelListener.isDebug()) {
cause.printStackTrace();
}
super.exceptionCaught(ctx, cause);
}
protected void encodeWirePacket(WirePacket packet, ByteBuf output) throws Exception {
private void encodeWirePacket(WirePacket packet, ByteBuf output) {
packet.writeId(output);
packet.writeBytes(output);
}
@@ -414,9 +398,8 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
* @param ctx - the current context.
* @param packet - the packet to encode to a byte array.
* @param output - the output byte array.
* @throws Exception If anything went wrong.
*/
protected void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) throws Exception {
private void encode(ChannelHandlerContext ctx, Object packet, ByteBuf output) {
NetworkMarker marker = null;
PacketEvent event = currentEvent;
@@ -445,6 +428,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
}
}
}
if (event != null) {
// Retrieve marker without accidentally constructing it
marker = NetworkMarker.getNetworkMarker(event);
@@ -464,7 +448,6 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
// Sent listeners?
finalEvent = event;
return;
}
} catch (Exception e) {
channelListener.getReporter().reportDetailed(this,
@@ -479,12 +462,9 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
}
/**
* Invoked when a packet has been written to the channel.
* @param ctx - current context.
* @param packet - the packet that has been written.
* @param promise - a promise.
* Invoked when a packet has been written to the channel
*/
protected void finalWrite(ChannelHandlerContext ctx, Object packet, ChannelPromise promise) {
private void finalWrite() {
PacketEvent event = finalEvent;
if (event != null) {
@@ -498,17 +478,15 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
private void scheduleMainThread(final Object packetCopy) {
// Don't use BukkitExecutors for this - it has a bit of overhead
Bukkit.getScheduler().scheduleSyncDelayedTask(factory.getPlugin(), new Runnable() {
@Override
public void run() {
Bukkit.getScheduler().scheduleSyncDelayedTask(factory.getPlugin(), () -> {
if (!closed) {
invokeSendPacket(packetCopy);
}
});
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuffer, List<Object> packets) throws Exception {
byteBuffer.markReaderIndex();
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuffer, List<Object> packets) {
DECODE_BUFFER.invoke(vanillaDecoder, ctx, byteBuffer, packets);
try {
@@ -534,12 +512,13 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
if (output != null) {
if (output.isCancelled()) {
it.remove();
continue;
} else if (output.getPacket().getHandle() != input) {
it.set(output.getPacket().getHandle());
}
} else {
if (output.getPacket().getHandle() != input) {
it.set(output.getPacket().getHandle());
}
finishQueue.addLast(output);
finishQueue.addLast(output);
}
}
}
} catch (Exception e) {
@@ -549,11 +528,9 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
}
/**
* Invoked after our decoder.
* @param ctx - current context.
* @param msg - the current packet.
* Invoked after our decoder
*/
protected void finishRead(ChannelHandlerContext ctx, Object msg) {
private void finishRead() {
// Assume same order
PacketEvent event = finishQueue.pollFirst();
@@ -571,7 +548,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
* @param packetClass - the packet class.
* @param packet - the packet.
*/
protected void handleLogin(Class<?> packetClass, Object packet) {
private void handleLogin(Class<?> packetClass, Object packet) {
// Try to find the login packet class
if (PACKET_LOGIN_CLIENT == null) {
PACKET_LOGIN_CLIENT = PacketType.Login.Client.START.getPacketClass();
@@ -593,7 +570,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
// Save the channel injector
factory.cacheInjector(profile.getName(), this);
}
if (PACKET_SET_PROTOCOL == null) {
try {
PACKET_SET_PROTOCOL = PacketType.Handshake.Client.SET_PROTOCOL.getPacketClass();
@@ -671,10 +648,12 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
* @param packet - the packet to send.
*/
private void invokeSendPacket(Object packet) {
Validate.isTrue(!closed, "cannot send packets to a closed channel");
// Attempt to send the packet with NetworkMarker.handle(), or the PlayerConnection if its active
try {
if (player instanceof Factory) {
MinecraftMethods.getNetworkManagerHandleMethod().invoke(networkManager, packet, new GenericFutureListener[0]);
MinecraftMethods.getNetworkManagerHandleMethod().invoke(networkManager, packet);
} else {
MinecraftMethods.getSendPacketMethod().invoke(getPlayerConnection(), packet);
}
@@ -689,15 +668,12 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
// TODO: Ensure the packet listeners are executed in the channel thread.
// Execute this in the channel thread
Runnable action = new Runnable() {
@Override
public void run() {
try {
MinecraftMethods.getNetworkManagerReadPacketMethod().invoke(networkManager, null, packet);
} catch (Exception e) {
// Inform the user
ProtocolLibrary.getErrorReporter().reportMinimal(factory.getPlugin(), "recieveClientPacket", e);
}
Runnable action = () -> {
try {
MinecraftMethods.getNetworkManagerReadPacketMethod().invoke(networkManager, null, packet);
} catch (Exception e) {
// Inform the user
ProtocolLibrary.getErrorReporter().reportMinimal(factory.getPlugin(), "recieveClientPacket", e);
}
};
@@ -724,8 +700,14 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
*/
private Object getPlayerConnection() {
if (playerConnection == null) {
playerConnection = MinecraftFields.getPlayerConnection(getPlayer());
Player player = getPlayer();
if (player == null) {
throw new IllegalStateException("cannot send packet to offline player" + (playerName != null ? " " + playerName : ""));
}
playerConnection = MinecraftFields.getPlayerConnection(player);
}
return playerConnection;
}
@@ -744,7 +726,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
@Override
public Player getPlayer() {
if (player == null && playerName != null) {
return Bukkit.getPlayer(playerName);
return Bukkit.getPlayerExact(playerName);
}
return player;
@@ -809,19 +791,16 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
// the worker thread is waiting for the main thread to finish executing PlayerQuitEvent.
//
// TL;DR: Concurrency is hard.
executeInChannelThread(new Runnable() {
@Override
public void run() {
String[] handlers = new String[] {
"protocol_lib_decoder", "protocol_lib_finish", "protocol_lib_encoder"
};
executeInChannelThread(() -> {
String[] handlers = new String[] {
"protocol_lib_decoder", "protocol_lib_finish", "protocol_lib_encoder"
};
for (String handler : handlers) {
try {
originalChannel.pipeline().remove(handler);
} catch (NoSuchElementException e) {
// Ignore
}
for (String handler : handlers) {
try {
originalChannel.pipeline().remove(handler);
} catch (NoSuchElementException e) {
// Ignore
}
}
});
@@ -844,15 +823,12 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
* @param command - the command to execute.
*/
private void executeInChannelThread(final Runnable command) {
originalChannel.eventLoop().execute(new Runnable() {
@Override
public void run() {
try {
command.run();
} catch (Exception e) {
ProtocolLibrary.getErrorReporter().reportDetailed(ChannelInjector.this,
originalChannel.eventLoop().execute(() -> {
try {
command.run();
} catch (Exception e) {
ProtocolLibrary.getErrorReporter().reportDetailed(ChannelInjector.this,
Report.newBuilder(REPORT_CANNOT_EXECUTE_IN_CHANNEL_THREAD).error(e).build());
}
}
});
}
@@ -863,7 +839,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
* @param clazz - the type.
* @return The first handler, or NULL.
*/
public static ChannelHandler findChannelHandler(Channel channel, Class<?> clazz) {
static ChannelHandler findChannelHandler(Channel channel, Class<?> clazz) {
for (Entry<String, ChannelHandler> entry : channel.pipeline()) {
if (clazz.isAssignableFrom(entry.getValue().getClass())) {
return entry.getValue();
@@ -879,27 +855,27 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
public static class ChannelSocketInjector implements SocketInjector {
private final ChannelInjector injector;
public ChannelSocketInjector(ChannelInjector injector) {
ChannelSocketInjector(ChannelInjector injector) {
this.injector = Preconditions.checkNotNull(injector, "injector cannot be NULL");
}
@Override
public Socket getSocket() throws IllegalAccessException {
public Socket getSocket() {
return SocketAdapter.adapt((SocketChannel) injector.originalChannel);
}
@Override
public SocketAddress getAddress() throws IllegalAccessException {
public SocketAddress getAddress() {
return injector.originalChannel.remoteAddress();
}
@Override
public void disconnect(String message) throws InvocationTargetException {
public void disconnect(String message) {
injector.disconnect(message);
}
@Override
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) throws InvocationTargetException {
public void sendServerPacket(Object packet, NetworkMarker marker, boolean filtered) {
injector.sendServerPacket(packet, marker, filtered);
}
@@ -910,7 +886,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
@Override
public Player getUpdatedPlayer() {
return injector.updated;
return injector.updated != null ? injector.updated : getPlayer();
}
@Override
@@ -923,7 +899,7 @@ public class ChannelInjector extends ByteToMessageDecoder implements Injector {
injector.setPlayer(updatedPlayer);
}
public ChannelInjector getChannelInjector() {
ChannelInjector getChannelInjector() {
return injector;
}
}
@@ -71,7 +71,7 @@ public abstract class AbstractInputStreamLookup {
Player player = previous.getPlayer();
// Default implementation
if (player instanceof InjectorContainer) {
if (player instanceof TemporaryPlayer) {
TemporaryPlayerFactory.setInjectorInPlayer(player, current);
}
}
@@ -47,8 +47,8 @@ public class TemporaryPlayerFactory {
* @return The referenced player injector, or NULL if none can be found.
*/
public static SocketInjector getInjectorFromPlayer(Player player) {
if (player instanceof InjectorContainer) {
return ((InjectorContainer) player).getInjector();
if (player instanceof TemporaryPlayer) {
return ((TemporaryPlayer) player).getInjector();
}
return null;
}
@@ -59,7 +59,7 @@ public class TemporaryPlayerFactory {
* @param injector - the injector to store.
*/
public static void setInjectorInPlayer(Player player, SocketInjector injector) {
((InjectorContainer) player).setInjector(injector);
((TemporaryPlayer) player).setInjector(injector);
}
/**
@@ -88,7 +88,7 @@ public class TemporaryPlayerFactory {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
String methodName = method.getName();
SocketInjector injector = ((InjectorContainer) obj).getInjector();
SocketInjector injector = ((TemporaryPlayer) obj).getInjector();
if (injector == null)
throw new IllegalStateException("Unable to find injector.");
@@ -152,7 +152,7 @@ public class TemporaryPlayerFactory {
public int accept(Method method) {
// Do not override the object method or the superclass methods
if (method.getDeclaringClass().equals(Object.class) ||
method.getDeclaringClass().equals(InjectorContainer.class))
method.getDeclaringClass().equals(TemporaryPlayer.class))
return 0;
else
return 1;
@@ -162,7 +162,7 @@ public class TemporaryPlayerFactory {
// CGLib is amazing
Enhancer ex = new Enhancer();
ex.setSuperclass(InjectorContainer.class);
ex.setSuperclass(TemporaryPlayer.class);
ex.setInterfaces(new Class[] { Player.class });
ex.setCallbacks(new Callback[] { NoOp.INSTANCE, implementation });
ex.setCallbackFilter(callbackFilter);
@@ -179,7 +179,7 @@ public class TemporaryPlayerFactory {
public Player createTemporaryPlayer(Server server, SocketInjector injector) {
Player temporary = createTemporaryPlayer(server);
((InjectorContainer) temporary).setInjector(injector);
((TemporaryPlayer) temporary).setInjector(injector);
return temporary;
}
File diff suppressed because it is too large. Load diff
@@ -21,54 +21,49 @@ import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.plugin.Plugin;
import com.comphenix.protocol.ProtocolLib;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketListener;
import com.comphenix.protocol.metrics.Metrics.Graph;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.plugin.Plugin;
public class Statistics {
// Metrics
private Metrics metrics;
public Statistics(Plugin plugin) throws IOException {
public Statistics(ProtocolLib plugin) throws IOException {
metrics = new Metrics(plugin);
metrics.logFailedRequests(plugin.getProtocolConfig().isDebug());
// Determine who is using this library
addPluginUserGraph(metrics);
metrics.start();
}
private void addPluginUserGraph(Metrics metrics) {
Graph pluginUsers = metrics.createGraph("Plugin Users");
for (Map.Entry<String, Integer> entry : getPluginUsers(ProtocolLibrary.getProtocolManager()).entrySet()) {
final int count = entry.getValue();
// Plot plugins of this type
pluginUsers.addPlotter(new Metrics.Plotter(entry.getKey()) {
@Override
public int getValue() {
return count;
}
});
metrics.addCustomChart(new Metrics.AdvancedPie("Plugin Users", this::getPluginUsers));
metrics.addCustomChart(new Metrics.SimplePie("buildVersion", () -> splitVersion().getRight()));
}
public static Pair<String, String> splitVersion() {
String version = ProtocolLibrary.getPlugin().getDescription().getVersion();
if (version.contains("-b")) {
String[] split = version.split("-b");
return Pair.of(split[0], split[1]);
} else {
return Pair.of(version, "Unknown");
}
}
// Retrieve loaded plugins
private Map<String, Integer> getPluginUsers(ProtocolManager manager) {
Map<String, Integer> users = new HashMap<String, Integer>();
for (PacketListener listener : manager.getPacketListeners()) {
private Map<String, Integer> getPluginUsers() {
Map<String, Integer> users = new HashMap<>();
for (PacketListener listener : ProtocolLibrary.getProtocolManager().getPacketListeners()) {
String name = PacketAdapter.getPluginName(listener);
// Increment occurence
if (!users.containsKey(name)) {
users.put(name, 1);
@@ -76,7 +71,7 @@ public class Statistics {
users.put(name, users.get(name) + 1);
}
}
return users;
}
}
@@ -7,19 +7,13 @@
// Somewhat modified by aadnk.
package com.comphenix.protocol.updater;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.error.Report;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
@@ -27,9 +21,6 @@ import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.error.Report;
/**
* Check dev.bukkit.org to find updates for a given plugin, and download the updates if needed.
* <p/>
@@ -60,7 +51,7 @@ public class BukkitUpdater extends Updater {
private static final String VERSION_VALUE = "gameVersion"; // Gets remote file's build version
private static final Object FILE_NAME = "fileName"; // Gets remote file's name
private static final String QUERY = "/servermods/files?projectIds="; // Path to GET
private static final String HOST = "https://api.curseforge.com"; // Slugs will be appended to this to get to the project's RSS feed
private static final String HOST = "https://servermods.forgesvc.net"; // Formerly api.curseforge.net
// private static final String[] NO_UPDATE_TAG = { "-DEV", "-PRE", "-SNAPSHOT" }; // If the version number contains one of these, don't update.
private static final int BYTE_SIZE = 1024; // Used for downloading files
@@ -71,7 +62,7 @@ public class BukkitUpdater extends Updater {
/**
* Initialize the updater.
* <p>
* Call {@link #start()} to actually start looking (and downloading) updates.
* Call {@link #start(UpdateType)} to actually start looking (and downloading) updates.
*
* @param plugin The plugin that is checking for an update.
* @param id The dev.bukkit.org id of the project
@@ -186,18 +177,6 @@ public class BukkitUpdater extends Updater {
this.plugin.getLogger().info("Downloading update: " + percent + "% of " + fileLength + " bytes.");
}
}
//Just a quick check to make sure we didn't leave any files from last time...
for (final File xFile : new File(this.plugin.getDataFolder().getParent(), this.updateFolder).listFiles()) {
if (xFile.getName().endsWith(".zip")) {
xFile.delete();
}
}
// Check to see if it's a zip file, if it is, unzip it.
final File dFile = new File(folder.getAbsolutePath() + "/" + file);
if (dFile.getName().endsWith(".zip")) {
// Unzip
this.unzip(dFile.getCanonicalPath());
}
if (this.announce) {
this.plugin.getLogger().info("Finished updating.");
}
@@ -217,107 +196,6 @@ public class BukkitUpdater extends Updater {
}
}
/**
* Part of Zip-File-Extractor, modified by Gravity for use with Bukkit
*/
private void unzip(String file) {
try {
final File fSourceZip = new File(file);
final String zipPath = file.substring(0, file.length() - 4);
ZipFile zipFile = new ZipFile(fSourceZip);
Enumeration<? extends ZipEntry> e = zipFile.entries();
while (e.hasMoreElements()) {
ZipEntry entry = e.nextElement();
File destinationFilePath = new File(zipPath, entry.getName());
destinationFilePath.getParentFile().mkdirs();
if (entry.isDirectory()) {
continue;
} else {
final BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(entry));
int b;
final byte buffer[] = new byte[BukkitUpdater.BYTE_SIZE];
final FileOutputStream fos = new FileOutputStream(destinationFilePath);
final BufferedOutputStream bos = new BufferedOutputStream(fos, BukkitUpdater.BYTE_SIZE);
while ((b = bis.read(buffer, 0, BukkitUpdater.BYTE_SIZE)) != -1) {
bos.write(buffer, 0, b);
}
bos.flush();
bos.close();
bis.close();
final String name = destinationFilePath.getName();
if (name.endsWith(".jar") && this.pluginFile(name)) {
destinationFilePath.renameTo(new File(this.plugin.getDataFolder().getParent(), this.updateFolder + "/" + name));
}
}
entry = null;
destinationFilePath = null;
}
e = null;
zipFile.close();
zipFile = null;
// Move any plugin data folders that were included to the right place, Bukkit won't do this for us.
for (final File dFile : new File(zipPath).listFiles()) {
if (dFile.isDirectory()) {
if (this.pluginFile(dFile.getName())) {
final File oFile = new File(this.plugin.getDataFolder().getParent(), dFile.getName()); // Get current dir
final File[] contents = oFile.listFiles(); // List of existing files in the current dir
for (final File cFile : dFile.listFiles()) // Loop through all the files in the new dir
{
boolean found = false;
for (final File xFile : contents) // Loop through contents to see if it exists
{
if (xFile.getName().equals(cFile.getName())) {
found = true;
break;
}
}
if (!found) {
// Move the new file into the current dir
cFile.renameTo(new File(oFile.getCanonicalFile() + "/" + cFile.getName()));
} else {
// This file already exists, so we don't need it anymore.
cFile.delete();
}
}
}
}
dFile.delete();
}
new File(zipPath).delete();
fSourceZip.delete();
} catch (final IOException ex) {
this.plugin.getLogger().warning("The auto-updater tried to unzip a new update file, but was unsuccessful.");
this.result = BukkitUpdater.UpdateResult.FAIL_DOWNLOAD;
ex.printStackTrace();
}
new File(file).delete();
}
/**
* Check if the name of a jar is one of the plugins currently installed, used for extracting the correct files out of a zip.
*/
private boolean pluginFile(String name) {
for (final File file : new File("plugins").listFiles()) {
if (file.getName().equals(name)) {
return true;
}
}
return false;
}
/**
* Evaluate whether the version number is marked showing that it should not be updated by this program
*/
/* private boolean hasTag(String version) {
for (final String string : BukkitUpdater.NO_UPDATE_TAG) {
if (version.contains(string)) {
return true;
}
}
return false;
} */
public boolean read() {
try {
final URLConnection conn = this.url.openConnection();
@@ -331,10 +209,9 @@ public class BukkitUpdater extends Updater {
final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
final String response = reader.readLine();
final JSONArray array = response != null ? (JSONArray) JSONValue.parse(response) : null;
final JSONArray array = (JSONArray) JSONValue.parse(response);
if (array.size() == 0) {
if (array == null || array.size() == 0) {
this.plugin.getLogger().warning("The updater could not find any files for the project id " + this.id);
this.result = UpdateResult.FAIL_BADID;
return false;
@@ -97,9 +97,7 @@ public final class SpigotUpdater extends Updater {
private static final byte[] API_KEY = ("key=98BE0FE67F88AB82B4C197FAF1DC3B69206EFDCC4D3B80FC83A00037510B99B4&resource=" + ID).getBytes(Charsets.UTF_8);
public String getSpigotVersion() throws IOException {
Closer closer = Closer.create();
try {
try (Closer closer = Closer.create()) {
HttpURLConnection con = (HttpURLConnection) new URL(API_URL).openConnection();
con.setDoOutput(true);
con.setRequestMethod(ACTION);
@@ -108,8 +106,6 @@ public final class SpigotUpdater extends Updater {
InputStreamReader isr = closer.register(new InputStreamReader(con.getInputStream()));
BufferedReader br = closer.register(new BufferedReader(isr));
return br.readLine();
} finally {
closer.close();
}
}
@@ -1,5 +1,6 @@
name: ProtocolLib
version: ${project.fullVersion}
#version: ${project.fullVersion}
version: 4.4.0
description: Provides read/write access to the Minecraft protocol.
authors: [dmulloy2, comphenix]
@@ -1,48 +1,66 @@
package com.comphenix.protocol;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.logging.Logger;
import net.minecraft.server.v1_12_R1.DispenserRegistry;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.craftbukkit.v1_12_R1.CraftServer;
import org.bukkit.craftbukkit.v1_12_R1.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_12_R1.util.Versioning;
import com.comphenix.protocol.utility.Constants;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion;
import net.minecraft.server.v1_13_R2.DispenserRegistry;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.craftbukkit.v1_13_R2.CraftServer;
import org.bukkit.craftbukkit.v1_13_R2.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v1_13_R2.util.Versioning;
import static org.mockito.Mockito.*;
/**
* Used to ensure that ProtocolLib and Bukkit is prepared to be tested.
*
* @author Kristian
*/
public class BukkitInitialization {
private static boolean initialized;
private static boolean packaged;
private static final BukkitInitialization instance = new BukkitInitialization();
private BukkitInitialization() {
System.out.println("Created new BukkitInitialization on " + Thread.currentThread().getName());
}
private boolean initialized;
private boolean packaged;
public static synchronized void initializePackage() {
instance.setPackage();
}
public static synchronized void initializeItemMeta() {
instance.initialize();
}
/**
* Initialize Bukkit and ProtocolLib such that we can perfrom unit testing.
* @throws IllegalAccessException If we are unable to initialize Bukkit.
* Initialize Bukkit and ProtocolLib such that we can perfrom unit testing
*/
public static void initializeItemMeta() throws IllegalAccessException {
private void initialize() {
if (!initialized) {
// Denote that we're done
initialized = true;
initializePackage();
try {
LogManager.getLogger();
} catch (Throwable ex) {
// Happens only on my Jenkins, but if it errors here it works when it matters
ex.printStackTrace();
}
DispenserRegistry.c(); // Basically registers everything
// Mock the server object
Server mockedServer = mock(Server.class);
when(mockedServer.getLogger()).thenReturn(Logger.getLogger("Minecraft"));
when(mockedServer.getLogger()).thenReturn(java.util.logging.Logger.getLogger("Minecraft"));
when(mockedServer.getName()).thenReturn("Mock Server");
when(mockedServer.getVersion()).thenReturn(CraftServer.class.getPackage().getImplementationVersion());
when(mockedServer.getBukkitVersion()).thenReturn(Versioning.getBukkitVersion());
@@ -58,12 +76,19 @@ public class BukkitInitialization {
/**
* Ensure that package names are correctly set up.
*/
public static void initializePackage() {
private void setPackage() {
if (!packaged) {
packaged = true;
try {
LogManager.getLogger();
} catch (Throwable ex) {
// Happens only on my Jenkins, but if it errors here it works when it matters
ex.printStackTrace();
}
MinecraftReflection.setMinecraftPackage(Constants.NMS, Constants.OBC);
MinecraftVersion.setCurrentVersion(MinecraftVersion.COLOR_UPDATE);
MinecraftVersion.setCurrentVersion(MinecraftVersion.AQUATIC_UPDATE);
}
}
}
}
@@ -23,6 +23,7 @@ import static org.junit.Assert.assertTrue;
import org.junit.Test;
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
import com.comphenix.protocol.utility.MinecraftVersion;
public class MinecraftVersionTest {
@@ -25,9 +25,9 @@ import com.comphenix.protocol.PacketType.Protocol;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import net.minecraft.server.v1_12_R1.EnumProtocol;
import net.minecraft.server.v1_12_R1.EnumProtocolDirection;
import net.minecraft.server.v1_12_R1.PacketLoginInStart;
import net.minecraft.server.v1_13_R2.EnumProtocol;
import net.minecraft.server.v1_13_R2.EnumProtocolDirection;
import net.minecraft.server.v1_13_R2.PacketLoginInStart;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -16,33 +16,32 @@
*/
package com.comphenix.protocol.events;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.*;
import net.minecraft.server.v1_12_R1.AttributeModifier;
import net.minecraft.server.v1_12_R1.DataWatcher;
import net.minecraft.server.v1_12_R1.Entity;
import net.minecraft.server.v1_12_R1.EntityLightning;
import net.minecraft.server.v1_12_R1.MobEffect;
import net.minecraft.server.v1_12_R1.MobEffectList;
import net.minecraft.server.v1_12_R1.PacketPlayOutBoss;
import net.minecraft.server.v1_12_R1.PacketPlayOutUpdateAttributes;
import net.minecraft.server.v1_12_R1.PacketPlayOutUpdateAttributes.AttributeSnapshot;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.injector.PacketConstructor;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.Util;
import com.comphenix.protocol.wrappers.BlockPosition;
import com.comphenix.protocol.wrappers.*;
import com.comphenix.protocol.wrappers.EnumWrappers.SoundCategory;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.google.common.collect.Lists;
import net.minecraft.server.v1_13_R2.*;
import net.minecraft.server.v1_13_R2.PacketPlayOutUpdateAttributes.AttributeSnapshot;
import org.apache.commons.lang.SerializationUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.bukkit.ChatColor;
import org.bukkit.DyeColor;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.WorldType;
@@ -55,27 +54,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.PacketType.Sender;
import com.comphenix.protocol.injector.PacketConstructor;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.Util;
import com.comphenix.protocol.wrappers.BlockPosition;
import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.EnumWrappers.SoundCategory;
import com.comphenix.protocol.wrappers.WrappedBlockData;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedWatchableObject;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import com.google.common.collect.Lists;
import static com.comphenix.protocol.utility.TestUtils.*;
import static org.junit.Assert.*;
// Ensure that the CraftItemFactory is mockable
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
@@ -87,7 +67,7 @@ public class PacketContainerTest {
private EquivalentConverter<ItemStack> itemConvert = BukkitConverters.getItemStackConverter();
@BeforeClass
public static void initializeBukkit() throws IllegalAccessException {
public static void initializeBukkit() {
BukkitInitialization.initializeItemMeta();
BukkitInitialization.initializePackage();
}
@@ -177,20 +157,20 @@ public class PacketContainerTest {
@Test
public void testGetStringArrays() {
PacketContainer tabComplete = new PacketContainer(PacketType.Play.Server.TAB_COMPLETE);
testObjectArray(tabComplete.getStringArrays(), 0, new String[0], new String[] { "hello", "world" });
PacketContainer packet = new PacketContainer(PacketType.Play.Client.UPDATE_SIGN);
testObjectArray(packet.getStringArrays(), 0, new String[0], new String[] { "hello", "world" });
}
@Test
public void testGetIntegerArrays() {
// Contains a byte array we will test
PacketContainer mapChunkBulk = new PacketContainer(PacketType.Play.Server.WORLD_PARTICLES);
StructureModifier<int[]> integers = mapChunkBulk.getIntegerArrays();
PacketContainer packet = new PacketContainer(PacketType.Play.Server.ENTITY_DESTROY);
StructureModifier<int[]> integers = packet.getIntegerArrays();
int[] testArray = new int[] { 1, 2, 3 };
// Pre and post conditions
assertArrayEquals(null, integers.read(0));
mapChunkBulk.getModifier().writeDefaults();
packet.getModifier().writeDefaults();
assertArrayEquals(new int[0], integers.read(0));
integers.write(0, testArray);
@@ -212,7 +192,7 @@ public class PacketContainerTest {
}
private ItemStack itemWithData() {
ItemStack item = new ItemStack(Material.WOOL, 1, DyeColor.GREEN.getWoolData());
ItemStack item = new ItemStack(Material.GREEN_WOOL, 1);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.GREEN + "Green Wool");
meta.setLore(Util.asList(ChatColor.WHITE + "This is lore."));
@@ -236,17 +216,7 @@ public class PacketContainerTest {
// Read back array
List<ItemStack> comparison = itemAccess.read(0);
assertEquals(items, comparison);
}
private boolean equivalentItem(ItemStack first, ItemStack second) {
if (first == null) {
return second == null;
} else if (second == null) {
return false;
} else {
return first.getType().equals(second.getType());
}
assertItemCollectionsEqual(items, comparison);
}
@Test
@@ -288,14 +258,11 @@ public class PacketContainerTest {
PacketContainer mobSpawnPacket = new PacketContainer(PacketType.Play.Server.SPAWN_ENTITY_LIVING);
StructureModifier<WrappedDataWatcher> watcherAccessor = mobSpawnPacket.getDataWatcherModifier();
Entity entity = new EntityLightning(null, 0, 0, 0, true);
DataWatcher watcher = entity.getDataWatcher();
WrappedDataWatcher dataWatcher = new WrappedDataWatcher(watcher);
dataWatcher.setObject(1, (byte) 1);
dataWatcher.setObject(2, 301);
dataWatcher.setObject(3, true);
dataWatcher.setObject(4, "Lorem");
WrappedDataWatcher dataWatcher = new WrappedDataWatcher();
dataWatcher.setObject(new WrappedDataWatcherObject(1, Registry.get(Byte.class)), (byte) 1);
dataWatcher.setObject(new WrappedDataWatcherObject(2, Registry.get(String.class)), "Lorem");
dataWatcher.setObject(new WrappedDataWatcherObject(3, Registry.get(Boolean.class)), true);
dataWatcher.setObject(new WrappedDataWatcherObject(4, Registry.getUUIDSerializer(true)), Optional.of(UUID.randomUUID()));
assertNull(watcherAccessor.read(0));
@@ -463,6 +430,7 @@ public class PacketContainerTest {
int e = 0;
if (effect.isAmbient()) e |= 1;
if (effect.hasParticles()) e |= 2;
if (mobEffect.f()) e |= 4;
assertEquals(e, (byte) packet.getBytes().read(2));
}
@@ -480,7 +448,7 @@ public class PacketContainerTest {
PacketContainer container = new PacketContainer(PacketType.Play.Server.NAMED_SOUND_EFFECT);
container.getSoundCategories().write(0, SoundCategory.PLAYERS);
assertEquals(container.getSoundCategories().read(0), SoundCategory.PLAYERS);
assertEquals(SoundCategory.PLAYERS, container.getSoundCategories().read(0));
}
@Test
@@ -499,17 +467,24 @@ public class PacketContainerTest {
assertEquals(container.getEnumModifier(Action.class, PacketPlayOutBoss.Action.class).read(0), Action.UPDATE_PCT);
}
@Test
public void testDimensionManager() {
PacketContainer container = new PacketContainer(PacketType.Play.Server.RESPAWN);
container.getDimensions().write(0, 1);
assertEquals((Object) 1, container.getDimensions().read(0));
}
/**
* Actions from the outbound Boss packet. Used for testing generic enums.
* @author dmulloy2
*/
public static enum Action {
public enum Action {
ADD,
REMOVE,
UPDATE_PCT,
UPDATE_NAME,
UPDATE_STYLE,
UPDATE_PROPERTIES;
UPDATE_PROPERTIES
}
@Test
@@ -526,15 +501,14 @@ public class PacketContainerTest {
}
private static final List<PacketType> BLACKLISTED = Util.asList(
PacketType.Play.Client.CUSTOM_PAYLOAD, PacketType.Play.Server.CUSTOM_PAYLOAD,
PacketType.Play.Server.SET_COOLDOWN
PacketType.Play.Server.TAGS
);
@Test
public void testDeepClone() {
// Try constructing all the packets
for (PacketType type : PacketType.values()) {
if (BLACKLISTED.contains(type) || type.isDeprecated()) {
if (BLACKLISTED.contains(type) || type.isDeprecated() || type.name().contains("CUSTOM_PAYLOAD")) {
continue;
}
@@ -554,8 +528,10 @@ public class PacketContainerTest {
// Make sure watchable collections can be cloned
if (type == PacketType.Play.Server.ENTITY_METADATA) {
constructed.getWatchableCollectionModifier().write(0, Util.asList(
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.get(Byte.class)), (byte) 1),
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.get(String.class)), "String"),
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.get(Byte.class)),
(byte) 1),
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.get(String.class)),
"String"),
new WrappedWatchableObject(new WrappedDataWatcherObject(0, Registry.get(Float.class)), 1.0F)
));
}
@@ -578,14 +554,7 @@ public class PacketContainerTest {
testEquality(firstMod.read(i), secondMod.read(i));
}
}
} catch (IllegalArgumentException e) {
if (!registered) {
e.printStackTrace();
} else {
// Something is very wrong
throw e;
}
} catch (Throwable ex) {
} catch (Exception ex) {
throw new RuntimeException("Failed to serialize packet " + type, ex);
}
}
@@ -603,12 +572,17 @@ public class PacketContainerTest {
}
}
if (a instanceof ItemStack || b instanceof ItemStack) {
assertItemsEqual((ItemStack) a, (ItemStack) b);
return;
}
if (a == null || b == null) {
if (a == null && b == null) {
return;
}
} else {
if (a.equals(b) || Objects.equals(a, b) || a.toString().equals(b.toString())) {
if (a.equals(b) || Objects.equals(a, b) || stringEquality(a, b)) {
return;
}
}
@@ -620,6 +594,15 @@ public class PacketContainerTest {
assertEquals(a, b);
}
private boolean stringEquality(Object a, Object b) {
try {
return a.toString().equals(b.toString());
} catch (Exception ex) {
// internal null pointers, usually
return false;
}
}
/**
* Get the underlying array as an object array.
* @param val - array wrapped as an Object.
@@ -31,7 +31,7 @@ public class WirePacketTest {
public void testPackets() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.CHAT);
packet.getChatTypes().write(0, ChatType.CHAT);
WirePacket wire = WirePacket.fromPacket(packet);
WirePacket handle = WirePacket.fromPacket(packet.getHandle());
assertEquals(wire, handle);
@@ -6,8 +6,8 @@ import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import java.util.List;
import net.minecraft.server.v1_12_R1.ItemStack;
import net.minecraft.server.v1_12_R1.NonNullList;
import net.minecraft.server.v1_13_R2.ItemStack;
import net.minecraft.server.v1_13_R2.NonNullList;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -15,7 +15,8 @@ public class MinecraftMethodsTest {
}
@Test
public void testSendPacketMethod() {
public void testSendPacketMethods() {
assertNotNull(MinecraftMethods.getSendPacketMethod());
assertNotNull(MinecraftMethods.getNetworkManagerHandleMethod());
}
}
@@ -1,146 +1,156 @@
package com.comphenix.protocol.utility;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import net.minecraft.server.v1_12_R1.ChatComponentText;
import net.minecraft.server.v1_12_R1.ChunkCoordIntPair;
import net.minecraft.server.v1_12_R1.DataWatcher;
import net.minecraft.server.v1_12_R1.IBlockData;
import net.minecraft.server.v1_12_R1.IChatBaseComponent;
import net.minecraft.server.v1_12_R1.IChatBaseComponent.ChatSerializer;
import net.minecraft.server.v1_12_R1.NBTCompressedStreamTools;
import net.minecraft.server.v1_12_R1.PacketPlayOutUpdateAttributes.AttributeSnapshot;
import net.minecraft.server.v1_12_R1.PlayerConnection;
import net.minecraft.server.v1_12_R1.ServerPing;
import net.minecraft.server.v1_12_R1.ServerPing.ServerData;
import net.minecraft.server.v1_12_R1.ServerPing.ServerPingPlayerSample;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import com.comphenix.protocol.BukkitInitialization;
import com.mojang.authlib.GameProfile;
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
@PowerMockIgnore({ "org.apache.log4j.*", "org.apache.logging.*", "org.bukkit.craftbukkit.libs.jline.*" })
public class MinecraftReflectionTest {
@BeforeClass
public static void initializeBukkit() throws IllegalAccessException {
BukkitInitialization.initializeItemMeta();
}
// Mocking objects
private interface FakeEntity {
public Entity getBukkitEntity();
}
private interface FakeBlock {
public Block getBukkitEntity();
}
@AfterClass
public static void undoMocking() {
// NOP
MinecraftReflection.minecraftPackage = null;
}
@Test
public void testBukkitMethod() {
FakeEntity entity = mock(FakeEntity.class);
FakeBlock block = mock(FakeBlock.class);
MinecraftReflection.getBukkitEntity(entity);
MinecraftReflection.getBukkitEntity(block);
verify(entity, times(1)).getBukkitEntity();
verify(block, times(1)).getBukkitEntity();
}
@Test(expected = IllegalArgumentException.class)
public void testIllegalClass() {
MinecraftReflection.getBukkitEntity("Hello");
}
@Test
public void testAttributeSnapshot() {
assertEquals(AttributeSnapshot.class, MinecraftReflection.getAttributeSnapshotClass());
}
@Test
public void testChatComponent() {
assertEquals(IChatBaseComponent.class, MinecraftReflection.getIChatBaseComponentClass());
}
@Test
public void testChatComponentText() {
assertEquals(ChatComponentText.class, MinecraftReflection.getChatComponentTextClass());
}
@Test
public void testChatSerializer() {
assertEquals(ChatSerializer.class, MinecraftReflection.getChatSerializerClass());
}
@Test
public void testChunkCoordIntPair() {
assertEquals(ChunkCoordIntPair.class, MinecraftReflection.getChunkCoordIntPair());
}
@Test
public void testIBlockData() {
assertEquals(IBlockData.class, MinecraftReflection.getIBlockDataClass());
}
@Test
public void testPlayerConnection() {
assertEquals(PlayerConnection.class, MinecraftReflection.getPlayerConnectionClass());
}
@Test
public void testServerPing() {
assertEquals(ServerPing.class, MinecraftReflection.getServerPingClass());
}
@Test
public void testServerPingPlayerSample() {
assertEquals(ServerPingPlayerSample.class, MinecraftReflection.getServerPingPlayerSampleClass());
}
@Test
public void testServerPingServerData() {
assertEquals(ServerData.class, MinecraftReflection.getServerPingServerDataClass());
}
@Test
public void testNbtStreamTools() {
assertEquals(NBTCompressedStreamTools.class, MinecraftReflection.getNbtCompressedStreamToolsClass());
}
@Test
public void testDataWatcherItem() {
assertEquals(DataWatcher.Item.class, MinecraftReflection.getDataWatcherItemClass());
}
@Test
public void testItemStacks() {
ItemStack stack = new ItemStack(Material.GOLD_SWORD);
Object nmsStack = MinecraftReflection.getMinecraftItemStack(stack);
assertEquals(stack, MinecraftReflection.getBukkitItemStack(nmsStack));
}
@Test
public void testGameProfile() {
assertEquals(GameProfile.class, MinecraftReflection.getGameProfileClass());
}
}
package com.comphenix.protocol.utility;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import net.minecraft.server.v1_13_R2.ChatComponentText;
import net.minecraft.server.v1_13_R2.ChunkCoordIntPair;
import net.minecraft.server.v1_13_R2.DataWatcher;
import net.minecraft.server.v1_13_R2.IBlockData;
import net.minecraft.server.v1_13_R2.IChatBaseComponent;
import net.minecraft.server.v1_13_R2.IChatBaseComponent.ChatSerializer;
import net.minecraft.server.v1_13_R2.NBTCompressedStreamTools;
import net.minecraft.server.v1_13_R2.PacketPlayOutUpdateAttributes.AttributeSnapshot;
import net.minecraft.server.v1_13_R2.PlayerConnection;
import net.minecraft.server.v1_13_R2.ServerPing;
import net.minecraft.server.v1_13_R2.ServerPing.ServerData;
import net.minecraft.server.v1_13_R2.ServerPing.ServerPingPlayerSample;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.v1_13_R2.inventory.CraftItemStack;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import com.comphenix.protocol.BukkitInitialization;
import com.mojang.authlib.GameProfile;
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
@PowerMockIgnore({ "org.apache.log4j.*", "org.apache.logging.*", "org.bukkit.craftbukkit.libs.jline.*" })
public class MinecraftReflectionTest {
@BeforeClass
public static void initializeBukkit() {
BukkitInitialization.initializeItemMeta();
}
// Mocking objects
private interface FakeEntity {
Entity getBukkitEntity();
}
private interface FakeBlock {
Block getBukkitEntity();
}
@AfterClass
public static void undoMocking() {
// NOP
MinecraftReflection.minecraftPackage = null;
}
@Test
public void testBukkitMethod() {
FakeEntity entity = mock(FakeEntity.class);
FakeBlock block = mock(FakeBlock.class);
MinecraftReflection.getBukkitEntity(entity);
MinecraftReflection.getBukkitEntity(block);
verify(entity, times(1)).getBukkitEntity();
verify(block, times(1)).getBukkitEntity();
}
@Test(expected = IllegalArgumentException.class)
public void testIllegalClass() {
MinecraftReflection.getBukkitEntity("Hello");
}
@Test
public void testNullable() {
assertNull(MinecraftReflection.getNullableNMS("ProtocolLib"));
}
@Test
public void testAttributeSnapshot() {
assertEquals(AttributeSnapshot.class, MinecraftReflection.getAttributeSnapshotClass());
}
@Test
public void testChatComponent() {
assertEquals(IChatBaseComponent.class, MinecraftReflection.getIChatBaseComponentClass());
}
@Test
public void testChatComponentText() {
assertEquals(ChatComponentText.class, MinecraftReflection.getChatComponentTextClass());
}
@Test
public void testChatSerializer() {
assertEquals(ChatSerializer.class, MinecraftReflection.getChatSerializerClass());
}
@Test
public void testChunkCoordIntPair() {
assertEquals(ChunkCoordIntPair.class, MinecraftReflection.getChunkCoordIntPair());
}
@Test
public void testIBlockData() {
assertEquals(IBlockData.class, MinecraftReflection.getIBlockDataClass());
}
@Test
public void testPlayerConnection() {
assertEquals(PlayerConnection.class, MinecraftReflection.getPlayerConnectionClass());
}
@Test
public void testServerPing() {
assertEquals(ServerPing.class, MinecraftReflection.getServerPingClass());
}
@Test
public void testServerPingPlayerSample() {
assertEquals(ServerPingPlayerSample.class, MinecraftReflection.getServerPingPlayerSampleClass());
}
@Test
public void testServerPingServerData() {
assertEquals(ServerData.class, MinecraftReflection.getServerPingServerDataClass());
}
@Test
public void testNbtStreamTools() {
assertEquals(NBTCompressedStreamTools.class, MinecraftReflection.getNbtCompressedStreamToolsClass());
}
@Test
public void testDataWatcherItem() {
assertEquals(DataWatcher.Item.class, MinecraftReflection.getDataWatcherItemClass());
}
@Test
public void testItemStacks() {
ItemStack stack = new ItemStack(Material.GOLDEN_SWORD);
Object nmsStack = MinecraftReflection.getMinecraftItemStack(stack);
assertItemsEqual(stack, MinecraftReflection.getBukkitItemStack(nmsStack));
// The NMS handle for CraftItemStack is null with Material.AIR, make sure it is handled correctly
assertNotNull(MinecraftReflection.getMinecraftItemStack(CraftItemStack.asCraftCopy(new ItemStack(Material.AIR))));
}
@Test
public void testGameProfile() {
assertEquals(GameProfile.class, MinecraftReflection.getGameProfileClass());
}
}
@@ -1,17 +1,14 @@
package com.comphenix.protocol.utility;
import static org.junit.Assert.assertEquals;
import java.io.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import net.minecraft.server.v1_12_R1.IntHashMap;
import net.minecraft.server.v1_13_R2.IntHashMap;
import org.bukkit.ChatColor;
import org.bukkit.DyeColor;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
@@ -20,9 +17,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static org.junit.Assert.assertEquals;
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
@PowerMockIgnore({ "org.apache.log4j.*", "org.apache.logging.*", "org.bukkit.craftbukkit.libs.jline.*" })
@@ -30,7 +26,7 @@ import com.comphenix.protocol.wrappers.nbt.NbtFactory;
public class StreamSerializerTest {
@BeforeClass
public static void initializeBukkit() throws IllegalAccessException {
public static void initializeBukkit() {
BukkitInitialization.initializeItemMeta();
}
@@ -80,13 +76,13 @@ public class StreamSerializerTest {
String serialized = serializer.serializeItemStack(initial);
ItemStack deserialized = serializer.deserializeItemStack(serialized);
assertEquals(initial, deserialized);
assertItemsEqual(initial, deserialized);
}
@Test
public void testItemMeta() throws IOException {
StreamSerializer serializer = new StreamSerializer();
ItemStack initial = new ItemStack(Material.WOOL, 2, DyeColor.BLUE.getWoolData());
ItemStack initial = new ItemStack(Material.BLUE_WOOL, 2);
ItemMeta meta = initial.getItemMeta();
meta.setDisplayName(ChatColor.BLUE + "Blue Wool");
@@ -95,6 +91,6 @@ public class StreamSerializerTest {
String serialized = serializer.serializeItemStack(initial);
ItemStack deserialized = serializer.deserializeItemStack(serialized);
assertEquals(initial, deserialized);
assertItemsEqual(initial, deserialized);
}
}
@@ -0,0 +1,45 @@
package com.comphenix.protocol.utility;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.inventory.ItemStack;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
public class TestUtils {
public static void assertItemCollectionsEqual(List<ItemStack> first, List<ItemStack> second) {
assertEquals(first.size(), second.size());
for (int i = 0; i < first.size(); i++) {
assertItemsEqual(first.get(i), second.get(i));
}
}
public static void assertItemsEqual(ItemStack first, ItemStack second) {
if (first == null) {
assertNull(second);
} else {
assertNotNull(first);
// The legacy check in ItemStack#isSimilar causes a null pointer
assertEquals(first.getType(), second.getType());
assertEquals(first.getDurability(), second.getDurability());
assertEquals(first.hasItemMeta(), second.hasItemMeta());
if (first.hasItemMeta()) {
assertTrue(Bukkit.getItemFactory().equals(first.getItemMeta(), second.getItemMeta()));
}
}
}
public static boolean equivalentItem(ItemStack first, ItemStack second) {
if (first == null) {
return second == null;
} else if (second == null) {
return false;
} else {
return first.getType().equals(second.getType());
}
}
}
@@ -0,0 +1,42 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.reflect.EquivalentConverter;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.junit.BeforeClass;
import org.junit.Test;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class BukkitConvertersTest {
@BeforeClass
public static void beforeClass() {
BukkitInitialization.initializeItemMeta();
}
@Test
public void testItemStacks() {
ItemStack item = new ItemStack(Material.DIAMOND_SWORD, 16);
item.addEnchantment(Enchantment.DAMAGE_ALL, 4);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(ChatColor.GREEN + "Diamond Sword");
item.setItemMeta(meta);
EquivalentConverter<ItemStack> converter = BukkitConverters.getItemStackConverter();
Object nmsStack = converter.getGeneric(item);
ItemStack back = converter.getSpecific(nmsStack);
assertEquals(item.getType(), back.getType());
assertEquals(item.getDurability(), back.getDurability());
assertEquals(item.hasItemMeta(), back.hasItemMeta());
assertTrue(Bukkit.getItemFactory().equals(item.getItemMeta(), back.getItemMeta()));
}
}
@@ -16,15 +16,15 @@ public class ChunkCoordIntPairTest {
@Test
public void test() {
net.minecraft.server.v1_12_R1.ChunkCoordIntPair pair = new net.minecraft.server.v1_12_R1.ChunkCoordIntPair(1, 2);
net.minecraft.server.v1_13_R2.ChunkCoordIntPair pair = new net.minecraft.server.v1_13_R2.ChunkCoordIntPair(1, 2);
ChunkCoordIntPair specific = ChunkCoordIntPair.getConverter().getSpecific(pair);
assertEquals(1, specific.getChunkX());
assertEquals(2, specific.getChunkZ());
net.minecraft.server.v1_12_R1.ChunkCoordIntPair roundtrip =
(net.minecraft.server.v1_12_R1.ChunkCoordIntPair) ChunkCoordIntPair.getConverter().
getGeneric(net.minecraft.server.v1_12_R1.ChunkCoordIntPair.class, specific);
net.minecraft.server.v1_13_R2.ChunkCoordIntPair roundtrip =
(net.minecraft.server.v1_13_R2.ChunkCoordIntPair) ChunkCoordIntPair.getConverter().
getGeneric(specific);
assertEquals(1, roundtrip.x);
assertEquals(2, roundtrip.z);
@@ -2,12 +2,12 @@ package com.comphenix.protocol.wrappers;
import static org.junit.Assert.assertEquals;
import net.minecraft.server.v1_12_R1.EntityHuman.EnumChatVisibility;
import net.minecraft.server.v1_12_R1.EnumDifficulty;
import net.minecraft.server.v1_12_R1.EnumGamemode;
import net.minecraft.server.v1_12_R1.EnumProtocol;
import net.minecraft.server.v1_12_R1.PacketPlayInClientCommand.EnumClientCommand;
import net.minecraft.server.v1_12_R1.PacketPlayInUseEntity.EnumEntityUseAction;
import net.minecraft.server.v1_13_R2.EntityHuman.EnumChatVisibility;
import net.minecraft.server.v1_13_R2.EnumDifficulty;
import net.minecraft.server.v1_13_R2.EnumGamemode;
import net.minecraft.server.v1_13_R2.EnumProtocol;
import net.minecraft.server.v1_13_R2.PacketPlayInClientCommand.EnumClientCommand;
import net.minecraft.server.v1_13_R2.PacketPlayInUseEntity.EnumEntityUseAction;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -55,7 +55,6 @@ public class EnumWrappersTest {
FieldAccessor accessor = Accessors.getFieldAccessor(target.getClass(), fieldName, true);
return (T) converter.getGeneric(
accessor.getField().getType(),
converter.getSpecific(accessor.get(target))
);
}
@@ -58,7 +58,7 @@ public class MultiBlockChangeTest {
MultiBlockChangeInfo[] array = { info, info };
EquivalentConverter<MultiBlockChangeInfo[]> converter = MultiBlockChangeInfo.getArrayConverter(chunk);
Object generic = converter.getGeneric(MinecraftReflection.getMultiBlockChangeInfoArrayClass(), array);
Object generic = converter.getGeneric(array);
MultiBlockChangeInfo[] back = converter.getSpecific(generic);
// Make sure our conversions are correct
@@ -43,7 +43,7 @@ public class PlayerInfoDataTest {
WrappedChatComponent displayName = WrappedChatComponent.fromText("Name's Name");
PlayerInfoData data = new PlayerInfoData(profile, 42, NativeGameMode.CREATIVE, displayName);
Object generic = PlayerInfoData.getConverter().getGeneric(MinecraftReflection.getPlayerInfoDataClass(), data);
Object generic = PlayerInfoData.getConverter().getGeneric(data);
PlayerInfoData back = PlayerInfoData.getConverter().getSpecific(generic);
assertEquals(data, back);
@@ -6,9 +6,9 @@ import static org.junit.Assert.assertTrue;
import java.util.List;
import net.minecraft.server.v1_12_R1.AttributeModifier;
import net.minecraft.server.v1_12_R1.PacketPlayOutUpdateAttributes;
import net.minecraft.server.v1_12_R1.PacketPlayOutUpdateAttributes.AttributeSnapshot;
import net.minecraft.server.v1_13_R2.AttributeModifier;
import net.minecraft.server.v1_13_R2.PacketPlayOutUpdateAttributes;
import net.minecraft.server.v1_13_R2.PacketPlayOutUpdateAttributes.AttributeSnapshot;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -16,15 +16,20 @@
*/
package com.comphenix.protocol.wrappers;
import static org.junit.Assert.assertEquals;
import com.comphenix.protocol.BukkitInitialization;
import net.minecraft.server.v1_13_R2.IBlockData;
import org.bukkit.DyeColor;
import org.bukkit.Material;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.type.GlassPane;
import org.bukkit.craftbukkit.v1_13_R2.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_13_R2.block.impl.CraftGlassPane;
import org.bukkit.craftbukkit.v1_13_R2.util.CraftMagicNumbers;
import org.junit.BeforeClass;
import org.junit.Test;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.utility.MinecraftReflection;
import static org.junit.Assert.assertEquals;
/**
* @author dmulloy2
@@ -33,24 +38,37 @@ import com.comphenix.protocol.utility.MinecraftReflection;
public class WrappedBlockDataTest {
@BeforeClass
public static void initializeBukkit() throws IllegalAccessException {
public static void initializeBukkit() {
BukkitInitialization.initializeItemMeta();
}
@Test
public void test() {
Material type = Material.WOOL;
int data = DyeColor.BLUE.getWoolData();
public void testMaterialCreation() {
Material type = Material.BLUE_WOOL;
WrappedBlockData wrapper = WrappedBlockData.createData(type, data);
WrappedBlockData wrapper = WrappedBlockData.createData(type);
assertEquals(wrapper.getType(), type);
assertEquals(wrapper.getData(), data);
//assertEquals(wrapper.getData(), data);
Object generic = BukkitConverters.getWrappedBlockDataConverter().getGeneric(MinecraftReflection.getIBlockDataClass(), wrapper);
Object generic = BukkitConverters.getWrappedBlockDataConverter().getGeneric(wrapper);
WrappedBlockData back = BukkitConverters.getWrappedBlockDataConverter().getSpecific(generic);
assertEquals(wrapper.getType(), back.getType());
assertEquals(wrapper.getData(), back.getData());
}
@Test
public void testDataCreation() {
IBlockData nmsData = CraftMagicNumbers.getBlock(Material.CYAN_STAINED_GLASS_PANE).getBlockData();
GlassPane data = (GlassPane) CraftBlockData.fromData(nmsData);
data.setFace(BlockFace.EAST, true);
WrappedBlockData wrapper = WrappedBlockData.createData(data);
assertEquals(wrapper.getType(), Material.CYAN_STAINED_GLASS_PANE);
GlassPane back = new CraftGlassPane((IBlockData) wrapper.getHandle());
assertEquals(back.hasFace(BlockFace.EAST), data.hasFace(BlockFace.EAST));
assertEquals(back.hasFace(BlockFace.SOUTH), data.hasFace(BlockFace.SOUTH));
}
}
@@ -16,25 +16,22 @@
*/
package com.comphenix.protocol.wrappers;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import java.util.UUID;
import net.minecraft.server.v1_12_R1.EntityLightning;
import org.bukkit.craftbukkit.v1_12_R1.entity.CraftLightningStrike;
import org.bukkit.entity.Entity;
import org.junit.BeforeClass;
import org.junit.Test;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Serializer;
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
import net.minecraft.server.v1_13_R2.EntityEgg;
import org.bukkit.craftbukkit.v1_13_R2.entity.CraftEgg;
import org.bukkit.craftbukkit.v1_13_R2.entity.CraftEntity;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* @author dmulloy2
*/
@@ -49,9 +46,9 @@ public class WrappedDataWatcherTest {
@Test
public void testBytes() {
// Create a fake lightning strike and get its watcher
EntityLightning lightning = new EntityLightning(null, 0, 0, 0, true);
Entity entity = new CraftLightningStrike(null, lightning);
WrappedDataWatcher wrapper = WrappedDataWatcher.getEntityWatcher(entity);
EntityEgg nmsEgg = new EntityEgg(null, 0, 0, 0);
CraftEntity craftEgg = new CraftEgg(null, nmsEgg);
WrappedDataWatcher wrapper = WrappedDataWatcher.getEntityWatcher(craftEgg);
WrappedWatchableObject watchable = wrapper.getWatchableObject(0);
WrappedDataWatcherObject object = watchable.getWatcherObject();
@@ -90,8 +87,8 @@ public class WrappedDataWatcherTest {
@Test
public void testSerializers() {
Serializer blockPos = Registry.get(net.minecraft.server.v1_12_R1.BlockPosition.class, false);
Serializer optionalBlockPos = Registry.get(net.minecraft.server.v1_12_R1.BlockPosition.class, true);
Serializer blockPos = Registry.get(net.minecraft.server.v1_13_R2.BlockPosition.class, false);
Serializer optionalBlockPos = Registry.get(net.minecraft.server.v1_13_R2.BlockPosition.class, true);
assertNotSame(blockPos, optionalBlockPos);
// assertNull(Registry.get(ItemStack.class, false));
@@ -0,0 +1,61 @@
package com.comphenix.protocol.wrappers;
import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import org.bukkit.Color;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.Particle.DustOptions;
import org.bukkit.inventory.ItemStack;
import org.junit.BeforeClass;
import org.junit.Test;
import static com.comphenix.protocol.utility.TestUtils.assertItemsEqual;
import static org.junit.Assert.assertEquals;
public class WrappedParticleTest {
@BeforeClass
public static void beforeClass() {
BukkitInitialization.initializeItemMeta();
}
@Test
public void testBlockData() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.WORLD_PARTICLES);
WrappedParticle before = WrappedParticle.create(Particle.BLOCK_CRACK,
WrappedBlockData.createData(Material.LAPIS_BLOCK));
packet.getNewParticles().write(0, before);
WrappedParticle after = packet.getNewParticles().read(0);
assertEquals(before.getParticle(), after.getParticle());
assertEquals(before.getData(), after.getData());
}
@Test
public void testItemStacks() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.WORLD_PARTICLES);
WrappedParticle before = WrappedParticle.create(Particle.ITEM_CRACK, new ItemStack(Material.FLINT_AND_STEEL));
packet.getNewParticles().write(0, before);
WrappedParticle after = packet.getNewParticles().read(0);
assertEquals(before.getParticle(), after.getParticle());
assertItemsEqual((ItemStack) before.getData(), (ItemStack) after.getData());
}
@Test
public void testRedstone() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.WORLD_PARTICLES);
WrappedParticle before = WrappedParticle.create(Particle.REDSTONE, new DustOptions(Color.BLUE, 1));
packet.getNewParticles().write(0, before);
WrappedParticle after = packet.getNewParticles().read(0);
assertEquals(before.getParticle(), after.getParticle());
DustOptions beforeDust = (DustOptions) before.getData();
DustOptions afterDust = (DustOptions) after.getData();
assertEquals(beforeDust.getColor(), afterDust.getColor());
assertEquals(beforeDust.getSize(), afterDust.getSize(), 0);
}
}
@@ -1,25 +0,0 @@
package com.comphenix.protocol.wrappers;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import com.comphenix.protocol.BukkitInitialization;
@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
@PowerMockIgnore({ "org.apache.log4j.*", "org.apache.logging.*", "org.bukkit.craftbukkit.libs.jline.*" })
//@PrepareForTest(CraftItemFactory.class)
public class WrappedWatchableObjectTest {
//@BeforeClass
public static void initializeBukkit() throws IllegalAccessException {
BukkitInitialization.initializeItemMeta();
}
//@Test
/* public void testItemStack() {
final ItemStack stack = new ItemStack(Material.GOLD_AXE);
final WrappedWatchableObject test = new WrappedWatchableObject(0, stack);
ItemStack value = (ItemStack) test.getValue();
assertEquals(value.getType(), stack.getType());
} */
}
@@ -26,8 +26,8 @@ import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import net.minecraft.server.v1_12_R1.ItemStack;
import net.minecraft.server.v1_12_R1.Items;
import net.minecraft.server.v1_13_R2.ItemStack;
import net.minecraft.server.v1_13_R2.Items;
import org.junit.BeforeClass;
import org.junit.Test;
+10 -11
View File
@@ -3,13 +3,12 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib-Parent</artifactId>
<version>v4-SNAPSHOT</version>
<version>4.4.0</version>
<name>ProtocolLib-Parent</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<minorVersion>4.3.0</minorVersion>
<spigotVersion>1.12-R0.1-SNAPSHOT</spigotVersion>
<spigotVersion>1.13.1-R0.1-SNAPSHOT</spigotVersion>
</properties>
<modules>
@@ -25,18 +24,18 @@
<distributionManagement>
<repository>
<id>dmulloy2-releases</id>
<url>http://repo.dmulloy2.net/content/repositories/releases/</url>
<url>http://repo.dmulloy2.net/nexus/repository/releases/</url>
</repository>
<snapshotRepository>
<id>dmulloy2-snapshots</id>
<url>http://repo.dmulloy2.net/content/repositories/snapshots/</url>
<url>http://repo.dmulloy2.net/nexus/repository/snapshots/</url>
</snapshotRepository>
</distributionManagement>
<repositories>
<repository>
<id>dmulloy2-repo</id>
<url>http://repo.dmulloy2.net/content/groups/public/</url>
<url>http://repo.dmulloy2.net/nexus/repository/public/</url>
</repository>
<repository>
<id>spigot-repo</id>
@@ -45,7 +44,7 @@
</repositories>
<dependencies>
<!-- Compile with the old version of Netty -->
<!-- Compile with the old version of Netty for backwards compat -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
@@ -67,7 +66,7 @@
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2.2</version>
<version>3.2.5</version>
<scope>compile</scope>
</dependency>
<dependency>
@@ -98,16 +97,16 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<version>3.7.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<version>3.0.1</version>
<configuration>
<failOnError>false</failOnError>
<encoding>ISO-8859-1</encoding>